题意
有(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 Ple 200000,0 le a_ile2100000000)
符号
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
");
else if (a[i] == 0) printf("%lld
", t * t + 2 * t * (n - t));
else printf("%lld
", ans[a[i]]);
}
return 0;
}