Luogu4245 【模板】任意模數NTT
阿新 • • 發佈:2020-08-03
https://www.luogu.com.cn/problem/P4245
拆係數\(FFT\)
把大數字拆成最後\(15\)位(二進位制下)和前面的位,直接\(FFT\),然後合併
\[(2^{15} a_i+b_i)(2^{15}c_i+d_i)=\\ 2^{30}a_i c_i+2^{15} (a_i d_i +b_i c_i)+b_i d_i \]
計算一下就好了
注意,本題對精度要求很高,儘量減少計算次數(一開始爆\(0\),還以為\(FFT\)炸了,傻了好久\(QAQ\))
最好用\(long \quad double\),雖然\(double\)可以過,執行較快,但還是很慌的(極容易\(WA\)
\(C++ Code:\)
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #define N 400005 #define D long double #define ll long long using namespace std; int n,m,p,q,l,s,rev[N]; const D Pi=acos(-1.0); ll g1,g2,g3,ans; struct virt { D x,y; virt (D xx=0.0,D yy=0.0) { x=xx,y=yy; } virt operator + (virt b) { return virt(x+b.x,y+b.y); } virt operator - (virt b) { return virt(x-b.x,y-b.y); } virt operator * (virt b) { return virt(x*b.x-y*b.y,x*b.y+y*b.x); } }q1,q2,q3,q4,a[N],b[N],c[N],d[N],g[2][25],W[N]; void FFT(virt *a,int t) { for (int i=0;i<s;i++) if (i<rev[i]) swap(a[i],a[rev[i]]); for (int mid=1,o=1;mid<s;mid <<=1,o++) for (int j=0;j<s;j+=(mid << 1)) for (int k=0;k<mid;k++) { virt x=a[j+k],y=W[mid+k]*a[j+k+mid]; a[j+k]=x+y; a[j+k+mid]=x-y; } if (t==-1) { reverse(a+1,a+s); D k=1.0/s; for (int i=0;i<s;i++) a[i]=a[i]*k; } } int main() { scanf("%d%d%d",&n,&m,&p); for (int i=0;i<=n;i++) scanf("%d",&q),a[i].x=q >> 15,b[i].x=q & ((1 << 15) - 1); for (int i=0;i<=m;i++) scanf("%d",&q),c[i].x=q >> 15,d[i].x=q & ((1 << 15) - 1); l=0,s=1; while (s<=n+m) { s <<=1; l++; } for (int i=0;i<s;i++) rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << (l - 1)); for(int i=1;i<s;i<<=1) for (int k=0;k<i;k++) W[i+k]=virt(cos(Pi*k/i),sin(Pi*k/i)); FFT(a,1); FFT(b,1); FFT(c,1); FFT(d,1); for (int i=0;i<s;i++) { q1=a[i],q2=b[i],q3=c[i],q4=d[i]; a[i]=q1*q3; b[i]=q1*q4+q2*q3; c[i]=q2*q4; } FFT(a,-1); FFT(b,-1); FFT(c,-1); for (int i=0;i<=n+m;i++) { g1=(ll)(a[i].x+0.5); g2=(ll)(b[i].x+0.5); g3=(ll)(c[i].x+0.5); g1=((g1%p) << 30)%p; g2=((g2%p) << 15)%p; g3=g3%p; ans=((g1+g2+g3)%p+p)%p; printf("%lld ",ans); } putchar('\n'); return 0; }