2018秦皇島ccpc-camp Steins;Gate (原根+FFT)
阿新 • • 發佈:2018-10-02
con else include complex otp sca max 原根 問題
因為給定的模數P保證是素數,所以P一定有原根.
根據原根的性質,若\(g\)是\(P\)的原根,則\(g^k\)能夠生成\([1,P-1]\)中所有的數,這樣的k一共有P-2個.
則\(a_i*a_j(mod\ P)=a_k\) 就可以轉化為\(g^i*g^j(mod\ P) = g^{i+j}(mod\ P)=g^k\).
問題轉化為了求有多少對有序的<i,j>滿足 \((i+j)(mod\ (P-1)) = k\).
求出原根後,對\([1,P-1]\)中的每個數編號, 統計每個編號出現的次數,然後FFT求卷積
要特判0,因為原根不會生成0.所以用總的有序對數-其他不含0的有序對數得到含0的有序對,這是0的答案;超過P-1的數肯定沒有符合的有序對.
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 2e5+5; bool notpri[maxn]; int pri[maxn],zyz[maxn]; typedef long long LL; void pre(int N){ notpri[1]=1; for(int i=2;i<N;++i){ if(!notpri[i]){ pri[++pri[0]]=i; } for(int j=1;j<=pri[0] && (LL)i*(LL)pri[j]<N;++j){ notpri[i*pri[j]]=1; if(i%pri[j]==0){ break; } } } } LL Quick_Pow(LL x,LL p,LL mod){ if(!p){ return 1ll; } LL res=Quick_Pow(x,p>>1,mod); res=res*res%mod; if((p&1ll)==1ll){ res=(x%mod*res)%mod; } return res; } int FindRoot(int x){/*求素奇數的最小原根,倘若x不是奇數,但是也有原根的話,將質 因子分解改成對phi(x)即可。倘若要求多個原根,直接接著暴力驗證即可*/ int tmp=x-1; for(int i=1;tmp && i<=pri[0];++i){ if(tmp%pri[i]==0){ zyz[++zyz[0]]=pri[i]; while(tmp%pri[i]==0){ tmp/=pri[i]; } } } for(int g=2;g<=x-1;++g){ bool flag=1; for(int i=1;i<=zyz[0];++i){ if(Quick_Pow((LL)g,(LL)((x-1)/zyz[i]),(LL)x)==1){ flag=0; break; } } if(flag){ return g; } } return 0; } const int MAXN = 4e5 + 10; const double PI = acos(-1.0); struct Complex{ double x, 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}; } } va[MAXN * 2 + MAXN / 2], vb[MAXN * 2 + MAXN / 2]; int lenth = 1, rev[MAXN * 2 + MAXN / 2]; int N, M; // f 和 g 的數量 //f g和 的系數 // 卷積結果 // 大數乘積 int f[MAXN],g[MAXN]; vector<LL> conv; vector<LL> multi; //f g void init() { int tim = 0; lenth = 1; conv.clear(), multi.clear(); memset(va, 0, sizeof va); memset(vb, 0, sizeof vb); while (lenth <= N + M - 2) lenth <<= 1, tim++; for (int i = 0; i < lenth; i++) rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (tim - 1)); } void FFT(Complex *A, const int fla) { for (int i = 0; i < lenth; i++){ if (i < rev[i]){ swap(A[i], A[rev[i]]); } } for (int i = 1; i < lenth; i <<= 1){ const Complex w = (Complex){cos(PI / i), fla * sin(PI / i)}; for (int j = 0; j < lenth; j += (i << 1)){ Complex K = (Complex){1, 0}; for (int k = 0; k < i; k++, K = K * w){ const Complex x = A[j + k], y = K * A[j + k + i]; A[j + k] = x + y; A[j + k + i] = x - y; } } } } void getConv(){ //求多項式 init(); for (int i = 0; i < N; i++) va[i].x = f[i]; for (int i = 0; i < M; i++) vb[i].x = g[i]; FFT(va, 1), FFT(vb, 1); for (int i = 0; i < lenth; i++) va[i] = va[i] * vb[i]; FFT(va, -1); for (int i = 0; i <= N + M - 2; i++) conv.push_back((LL)(va[i].x / lenth + 0.5)); } LL vz[MAXN]; int id[MAXN]; int cnt[MAXN]; int rnk[MAXN]; LL ans[MAXN]; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif pre(maxn-1); int n,P ; scanf("%d %d",&n, &P); int rt = FindRoot(P); memset(id,0,sizeof(id)); LL idx = 1; for(int i=0;i<P-1;++i){ //一共只有[0,P-2],P-1個id id[idx] = i; rnk[i]= idx; idx = idx*rt%P; } for(int i=1;i<=n;++i){ LL tmp; scanf("%lld",&tmp); vz[i] = tmp; tmp%=P; if(tmp==0) continue; //卷積中不考慮0的貢獻 cnt[id[tmp]]++; } N = M = P; for(int i=0;i<P-1;++i){ f[i] = g[i] = cnt[i]; } getConv(); int sz = conv.size(); for(int i=0;i<sz;++i){ LL tmp = conv[i]; ans[rnk[i%(P-1)]] += tmp; } LL tot = (LL)n*n; //全部的枚舉可能-不選0之外的組合 = 包含0的組合 for(int i=1;i<P;++i){ tot -= ans[i]; } ans[0] = tot; for(int i=1;i<=n;++i){ if(vz[i]>=P) printf("0\n"); else{ printf("%lld\n",ans[vz[i]]); } } return 0; }
2018秦皇島ccpc-camp Steins;Gate (原根+FFT)