【题意】请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5。 a,b中的元素均为小于等于100的非负整数。
【算法】快速傅里叶变换(FFT)处理卷积
【题解】题目要求:
$$C_k=\sum_{i=k}^{n-1}A_i*B_{i-k}$$
令B'为B的数组反转,转化成和为定值的形式,即:
$$C_k=\sum_{i=k}^{n-1}A_i*B'_{n+k-1-i}$$
因为:
$$A_i=0,i\in[n,n+k-1]$$
$$B_{n+k-1-i}=0,i\in[0,k-1]$$
所以可以扩展式子的上下界,即:
$$C_k=D_{n+k-1}=\sum_{i=0}^{n+k-1}A_i*B'_{n+k-1-i}$$
这就可以用FFT处理卷积了。
复杂度O(n log n)。
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int maxn=300010; const double PI=acos(-1); int n; struct cp{ double x,y; cp(double a,double b){x=a;y=b;} cp(){x=y=0;} cp operator + (cp a){return (cp){x+a.x,y+a.y};} cp operator - (cp a){return (cp){x-a.x,y-a.y};} cp operator * (cp a){return (cp){x*a.x-y*a.y,x*a.y+y*a.x};} }a[maxn],b[maxn]; void fft(cp *a,int n,int f){ int k=0; for(int i=0;i<n;i++){ if(i>k)swap(a[i],a[k]); for(int j=n>>1;(k^=j)<j;j>>=1); } for(int l=2;l<=n;l<<=1){ int m=l>>1; cp wn=(cp){cos(2*PI*f/l),sin(2*PI*f/l)}; for(cp *p=a;p!=a+n;p+=l){ cp w=(cp){1,0}; for(int i=0;i<m;i++){ cp t=p[i+m]*w; p[i+m]=p[i]-t; p[i]=p[i]+t; w=w*wn; } } } if(f==-1){for(int i=0;i<n;i++)a[i].x/=n;} } int main(){ scanf("%d",&n); for(int i=0;i<n;i++)scanf("%lf %lf",&a[i].x,&b[n-i-1].x); int N=1;while(N<n*2)N*=2; fft(a,N,1);fft(b,N,1); for(int i=0;i<N;i++)a[i]=a[i]*b[i];//2n fft(a,N,-1); for(int i=n-1;i<2*n-1;i++)printf("%d\n",(int)(a[i].x+0.1)); return 0; }
只要和为定值的形式就可以卷积,如果看着很奇怪就将其中一个数组下标变成i,然后扩展上下界。