Steins;Gate

传送门

给出一个序列(a),对于每一个(a_k),输出有多少个有序二元组(a_i imes a_jequiv (mod a_k))

这看一眼就像是fft问题,fft一般可以统计加法的情况,但这里是乘法。

原根可以把乘法变成加法,仔细一看模,是([2,200000])的质数,根据原根存在定理,奇质数肯定存在原根,2也存在原根。

求质数的原根,时间复杂度是(O(plogp))

然后利用原根打一个指标表,是(O(n))

然后再利用原根把所有的非零(a_i)转换为指标,把序列(a)的各项的指标作为系数累积。

最后把这个多项式进行自乘。

就能得到一个指标多项式。

原根可以把乘法变成加法,然后利用fft的性质求解

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template<typename T = long long> inline T read() {
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();} 
    return s * f;
}
const int N = 5e5 + 5;
namespace PrimitiveRoot {
    vector<int> PrimeFactor(int n) {//分解质因子
        vector<int> fac;
        for(int i = 2; i * i <= n; ++i) {
            if(n % i == 0){
                fac.push_back(i);
                while(n % i == 0) n /= i;
            }
        }
        if(n > 1) fac.push_back(n);
        return fac;
    }
    int gcd(int a, int b){
        return b == 0 ? a : gcd(b, a % b);
    }
    ll qpow(ll a, ll b, ll p){
        ll ans = 1; a %= p;
        while(b){
            if(b & 1) ans = ans * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return ans;
    }
    int phi(int n){
        return n - 1;
        int ans = n;
        for(int i = 2; i * i <= n; i++){
            if(n % i == 0){
                ans -= ans/i;
                while(n % i == 0) n /= i;
            }
        }
        if(n > 1) ans -= ans / n;
        return ans;
    }
    bool checkroot(int x) {
        if(x == 2 ||x == 4) return true;
        if(x % 4 == 0) return false;
        if(x % 2 == 0) x /= 2; 
        for(int i = 2; i * i <= x; ++i){
            if(x % i == 0){
                while(x % i == 0) x /= i;
                return x == 1;
            }
        }
        return true;
    }
    int PrimitiveRoot(int p) { // 求p最小的原根
        int nowphi = phi(p);
        std::vector<int> fac = PrimeFactor(nowphi);
        for(int g = 2; g < p; g++) {
            bool f = 1;
            for(int j = 0; j < fac.size(); j++)
                if(qpow(g, nowphi / fac[j], p) == 1) {f = 0; break; }
            if(f) return g;
        }
        return -1; // 没有原根
    }
    void solve(int x){ //得到全部的原根
        if(!checkroot(x)) {
            printf("0
");; return;
        }
        int nowphi = phi(x);
        int g = PrimitiveRoot(x);
        vector<int> ans; ans.push_back(g); // 所有原根
        for(int i = 2; i < nowphi; ++i){
            if(gcd(i, nowphi) == 1) ans.push_back(qpow(g, i, x));
        }
    }
};
struct Complex{
    double x, y;
    Complex(double x = 0, double y = 0):x(x),y(y){};
    inline Complex operator + (const Complex b) const {
        return Complex(x + b.x,y + b.y);
    }
    inline Complex operator - (const Complex b) const {
        return Complex(x - b.x,y - b.y);
    }
    inline Complex operator * (const Complex b) const {
        return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
    }
};
namespace FFT{
    const double Pi = acos(-1);
    const int N = 5e5 + 5; // 刚好比n大的2的k次方再乘2
    Complex F[N], G[N];
    int r[N], n, m;
    void FFT(Complex *a, int n, int inv){
        for(int i = 0; i < n; i++)
            if(r[i] > i) swap(a[r[i]], a[i]);
 
       for(int mid = 2; mid <= n; mid <<= 1){
            Complex x(cos(2 * Pi / mid), inv * sin(2 * Pi / mid));
            for(int i = 0; i < n; i += mid){
                Complex w(1,0);
                for(int j = i; j < i + (mid >> 1); j++, w = w * x){
                    Complex t1 = a[j],t2 = a[j + (mid >> 1)] * w;
                    a[j] = t1 + t2; a[j + (mid >> 1)] = t1 - t2;
                }
            }
        }
    }
    void mul(ll *a, ll *b, ll *c, int nn, int mm, int &k) {
        n = nn, m = mm;
        for(mm += nn, nn = 1; nn <= mm; nn *= 2);
        for(int i = 0; i <= nn; i++) F[i].x = F[i].y = G[i].x = G[i].y = 0;
        for(int i = 0; i <= n; i++) F[i].x = a[i];
        for(int i = 0; i <= m; i++) G[i].x = b[i];
        int l = 0;
        for(m += n, n = 1; n <= m; n *= 2, l++);
        for(int i = 0; i < n; i++)
            r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
        FFT(F, n, 1); FFT(G, n, 1);
        for(int i = 0; i < n; i++) F[i] = F[i] * G[i];
        FFT(F, n, -1);  k = m;
        for(int i = 0; i <= m; i++) {
            c[i] = (ll)(F[i].x / n + 0.5);
        }
    }
};
int I[N];
void I_table(int g, int p){
    int base = 1;
    for(int i = 1; i < p - 1; i++) {
        base = base * g % p;
        I[base] = i;
    }
}
int a[N], b[N];
ll xishu[N], ans[N], c[N];
int main() {
    int n = read(), p = read();
    I_table(PrimitiveRoot::PrimitiveRoot(p), p);
    for(int i = 1; i <= n; i++) a[i] = read();
    int tot = 0;
    for(int i = 1; i <= n; i++) {
        if(a[i] % p == 0) tot++;
        else xishu[I[a[i] % p]]++;
    }
    int m;
    FFT::mul(xishu, xishu, c, p - 1, p - 1, m);

    for(int i = 0; i < p * 2; i++) ans[i % (p - 1)] += c[i];
    for(int i = 1; i <= n; i++) {
        if(a[i] >= p) printf("0
");
        else if(a[i] == 0) printf("%lld
", 1ll * tot * (n - tot) * 2 + 1ll * tot * tot);
        else printf("%lld
", ans[I[a[i]]]);
    }
    return 0;
}
I‘m Stein, welcome to my blog
原文地址:https://www.cnblogs.com/Emcikem/p/14392274.html