BZOJ_2194_快速傅立叶之二_(FFT+卷积)
描述
http://www.lydsy.com/JudgeOnline/problem.php?id=2194
给出序列\(a[0],a[1],...,a[n-1]\)和\(b[0],b[1],...,b[n-1]\).
\(c[k]=\sum_{i=k}^{n-1}a[i]b[i-k]\).
求序列\(c[]\).
分析
这题就是BZOJ_3527_[ZJOI2014]_力_(FFT+卷积)的后半段...
我们来重新分析一下.
首先我们要知道卷积的标准形式:
$$c[i]=\sum_{j=0}^ia[j]b[i-j]$$
很明显这道题并不是卷积的形式,因为卷积是和一定,二这道题却是差一定.
我们其实可以画画图(我脑洞大)...
然后可以发现差一定的时候就是你+1,我也+1,你-1,我也-1.
但是如果我们把其中一个序列倒过来,就变成了你+1,我-1,你-1,我+1,就变成和一定的了!这一点灰常重要!
然后上次我推的那个太不自然,我们这次好好分析一下.
1.把a倒置.
把a倒置之前原式为(我们这里令\(n=n-1\),序列就是\(0~n\),方便一些)
$$\sum_{j=k}^na[i]b[i-k]$$
我们设倒置之后的序列为\(a'[]\),则有
$$原式\Longleftrightarrow\sum_{i=k}^na'[n-i][b[i-k]$$
换元,得到:
$$\sum_{i=0}^{n-k}a'[n-(i+k)]b[(i+k)-k]$$
即
$$\sum_{i=0}^{n-k}a'[n-i-k]b[i]$$
也就是:
$$c[k]=\sum_{i=0}^{n-k}a'[n-i-k]b[i]$$
如果我们设\(A[k]=\sum_{i=1}^ka'[k-i]b[i]\),那么就有:
$$c[k]=A[n-k]$$
这样我们求个卷积,然后倒过来输出就好了.
2.把b倒置
在网上看到好几篇题解都说是倒置b,但是自己推了好长时间都没有推出来,最后发现其中有一点奥妙...
倒置之前原式:
$$\sum_{i=k}^na[i]b[i-k]$$
我们设倒置之后的序列为\(b'[]\),则有:
$$原式\Longleftrightarrow\sum_{i=k}^na[i]b'[n-i+k]$$
换元,得到:
$$\sum_{i=0}^{n-k}a[i+k]b'[n_(i+k)+k]$$
也就是
$$\sum_{i=0}^{n-k}a[i+k]b'[n-i]$$.
可以发现和是定值\(n+k\),但是循环上界却只有\(n-k\).
我们想要得到的应该是:
$$\sum_{i=0}^{n+k}a[i+k]b'[n-i]$$.
我们得到的式子少了一部分.但是观察可以发现,我们得到的式子的循环上界是\(n-k\),对应\(a[n]b'[k]\).
继续向上的\(a[i]\)都为\(0\),而且都后的\(b[i]\)会越界(\(b[负数]\)).
所以这个就可以表示一个卷积了.
$$c[k]=\sum_{i=0}^{n+k}a[n+k-i]b'[i]$$
这个式子是根据原式表示一个卷积二构造出来的等价的式子,只是看起来比较方便而已.
我们设\(B[i]=\sum_{i=0}^ka[i]b[k-i]\).
这样就可以得到
$$c[k]=B[n+k]$$
倒置b的版本:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N=1e5+5,maxn=N<<2; 5 const double pi=acos(-1.0); 6 int n; 7 int rev[maxn]; 8 int f[N],f_[N],g[N],ans[N]; 9 struct cp{ 10 double r,i; 11 cp(double r=0,double i=0):r(r),i(i){} 12 cp operator + (const cp &x) const { return cp(r+x.r,i+x.i); } 13 cp operator - (const cp &x) const { return cp(r-x.r,i-x.i); } 14 cp operator * (const cp &x) const { return cp(r*x.r-i*x.i,r*x.i+i*x.r);} 15 }a[maxn],b[maxn],A[maxn]; 16 void brc(int &n){ 17 memset(rev,-1,sizeof rev); 18 int k=1,l=0; 19 while(k<n) k<<=1, l++; 20 n=k; 21 for(int i=1;i<n-1;i++){ 22 if(rev[i]!=-1) continue; 23 int x=i,y=0,m=l; 24 while(m--) y<<=1, y|=(x&1), x>>=1; 25 rev[i]=y, rev[y]=i; 26 } 27 } 28 void dft(cp *a,int n,int op){ 29 for(int i=1;i<n-1;i++) A[rev[i]]=a[i]; 30 for(int i=1;i<n-1;i++) a[i]=A[i]; 31 for(int m=2;m<=n;m<<=1){ 32 cp wn(cos(2.0*pi/m*op),sin(2.0*pi/m*op)); 33 for(int i=0;i<n;i+=m){ 34 cp w(1); int k=m>>1; 35 for(int j=0;j<k;j++){ 36 cp u=a[i+j],t=w*a[i+j+k]; 37 a[i+j]=u+t; 38 a[i+j+k]=u-t; 39 w=w*wn; 40 } 41 } 42 } 43 if(op==-1)for(int i=0;i<n;i++) a[i].r/=n; 44 } 45 void fft(int *x,int *y,int *ans,int la,int lb){ 46 int len=la+lb-1; 47 brc(len); 48 for(int i=0;i<n;i++) a[i]=cp(x[i]), b[i]=cp(y[i]); 49 dft(a,len,1); dft(b,len,1); 50 for(int i=0;i<len;i++) a[i]=a[i]*b[i]; 51 dft(a,len,-1); 52 for(int i=0;i<len;i++) ans[i]=a[i].r+0.5; 53 } 54 int main(){ 55 scanf("%d",&n); 56 for(int i=0;i<n;i++) scanf("%d%d",&f[i],&g[i]); 57 for(int i=0;i<n;i++) f_[i]=g[n-1-i]; 58 fft(f,f_,ans,n,n); 59 for(int i=n-1;i<n+n-1;i++) printf("%d\n",ans[i]); 60 return 0; 61 }