【题意】请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5。 a,b中的元素均为小于等于100的非负整数。
【算法】快速傅里叶变换(FFT)处理卷积
【题解】题意要求C[k]=ΣA[i]*B[i-k],i=k~n-1。
即C[k]=ΣA[i+k]*B[i],i=0~n-k-1。
这是差为定值的情况,将其中一个向量反转即可得到和为定值的卷积形式。
令B[i]=B[n-i-1]
C[k]=ΣA[i+k]*B[n-i-1],i=0~n-k-1。
和为n+k-1,根据卷积D[n+k-1]=ΣA[i+k]*B[n-i-1],i=0~n-k-1。
当i=[n-k,n+k-1]时,A[i+k]=0,所以没有影响。
用FFT处理卷积D即可。
最后C[0~n-1]对应D[n-1~2*n-2]。
复杂度O(n log n)。
#include<cstdio> #include<algorithm> #include<complex> #include<algorithm> using namespace std; const int maxn=300010; const double PI=acos(-1); int n,b1[maxn],b2[maxn]; complex<double>a1[maxn],a2[maxn]; namespace fft{ complex<double>o[maxn],oi[maxn]; void init(int n){ for(int k=0;k<n;k++)o[k]=complex<double>(cos(2*PI*k/n),sin(2*PI*k/n)),oi[k]=conj(o[k]); } void transform(complex<double>*a,int n,complex<double>*o){ int k=0; while((1<<k)<n)k++; for(int i=0;i<n;i++){ int t=0; for(int j=0;j<k;j++)if((1<<j)&i)t|=(1<<(k-j-1)); if(i<t)swap(a[i],a[t]); } for(int l=2;l<=n;l*=2){ int m=l/2; for(complex<double>*p=a;p!=a+n;p+=l){ for(int i=0;i<m;i++){ complex<double>t=o[n/l*i]*p[i+m]; p[i+m]=p[i]-t; p[i]+=t; } } } } void dft(complex<double>*a,int n){transform(a,n,o);} void idft(complex<double>*a,int n){transform(a,n,oi);for(int i=0;i<n;i++)a[i]/=n;} } void multply(){ int N=1; while(N<n+n)N*=2; fft::init(N);fft::dft(a1,N);fft::dft(a2,N); for(int i=0;i<N;i++)a1[i]*=a2[i]; fft::idft(a1,N); } int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&b1[i],&b2[i]); a1[i].real(b1[i]);a2[n-i-1].real(b2[i]); } multply(); for(int i=n-1;i<2*n-1;i++)printf("%d\n",(int)((floor)(a1[i].real()+0.5))); return 0; }