steins;Gate - 牛客

题意

\(n\)个数\(a_1,a_2,\dots,a_n\)。对于每一个\(a_k\),求有多少个有序二元组\((i,j)\)满足\((a_i * a_j) mod(P) = a_k\),其中\(P\)为一给定质数。

\(1 \le N \le 200000,2 \le P\le 200000,0 \le a_i\le2100000000\)

符号

g :\(P\)的原根

题解

因为\(a_i mod P = g^t,(0 < t < P )\),实际上转换成\([ 0, P - 1)\)要方便很多。所以\((a_i * a_j) mod(P) = ((a_i mod P) * (a_j mod P) mod(P)) = g^{t_1+t_2} mod (P) = a_k\)。将乘法变成了加法,\(FFT\)就能用上了,和\(hdu4609\)的处理方法是一样的,kuangbin聚聚写的很清楚,也算是一个套路了。为什么要把模\(P\)等于0的数扣出来,因为这样的数是不能用原根表示的。

代码

const int N = 600010;

struct Complex {
    double r,i;
    Complex(double real=0.0,double image=0.0) {
        r=real;
        i=image;
    }
    Complex operator +(const Complex o){return Complex(r+o.r,i+o.i);}
    Complex operator -(const Complex o){return Complex(r-o.r,i-o.i);}
    Complex operator *(const Complex o){return Complex(r*o.r-i*o.i,r*o.i+i*o.r);}
} b[N];

int rev(int id, int len) {
    int pos = 0;
    for (int i = 0; (1 << i) < len; ++i) {
        pos <<= 1;
        if (id & (1 << i)) pos |= 1;
    }
    return pos;
}

Complex A[N];

void FFT(Complex *a, int len, int DFT) {
    rep(i, 0, len) A[rev(i, len)] = a[i];
    for (int s = 1; (1 << s) <= len; ++s) {
        int m = (1 << s);
        Complex wm = Complex(cos(DFT * 2 * PI / m), sin(DFT * 2 * PI / m));
        for (int i = 0; i < len; i += m) {
            Complex w = Complex(1, 0);
            for (int j = 0; j < (m >> 1); ++j) {
                Complex t = A[i + j];
                Complex u = w * A[i + j + (m >> 1)];
                A[i + j] = t + u;
                A[i + j + (m >> 1)] = t - u;
                w = w * wm;
            }
        }
    }
    if (DFT == -1) rep(i, 0, len) A[i].r /= len, A[i].i /= len;
    rep(i, 0, len) a[i] = A[i];
}

int n, g, P;
int a[N], id[N];

LL ans[N];

bool check(int g) {
    LL t = 1;
    rep(i, 1, P - 1) {
        t = t * g % P;
        if (t == 1) return 0;
    }
    return 1;
}

int qpow(int x, int y) {
    int res = 1;
    for (; y; x = 1ll * x * x % P, y >>= 1) if (y & 1) res = 1ll * res * x % P;
    return res;
}

int main()
{
    sc(n), sc(P);
    for (int i = 2; ; ++i) if (check(i)) {
        g = i;
        break;
    }

    LL t = 1;
    rep(i, 1, P) {
        t = t * g % P;
        id[t] = i;
    }

    t = 0;
    Rep(i, 1, n) {
        sc(a[i]);
        if (a[i] % P == 0) t++;
        else b[id[a[i] % P]].r++;
    }

    int len = (1 << 19);

    FFT(b, len, 1);
    rep(i, 0, len) b[i] = b[i] * b[i];
    FFT(b, len, -1);

    // 当P = 2时,i = 0也有贡献
    rep(i, 0, len) ans[qpow(g, i)] += (LL)(b[i].r + 0.5);

    Rep(i, 1, n) {
        if (a[i] >= P) printf("0\n");
        else if (a[i] == 0) printf("%lld\n", t * t + 2 * t * (n - t));
        else printf("%lld\n", ans[a[i]]);
    }

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zgglj-com/p/9749041.html