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 }
View Code

 

posted @ 2016-06-13 17:55  晴歌。  阅读(263)  评论(0编辑  收藏  举报