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'[]),则有

$$原式Longleftrightarrowsum_{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'[]),则有:

$$原式Longleftrightarrowsum_{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
",ans[i]);
60     return 0;
61 }
View Code
原文地址:https://www.cnblogs.com/Sunnie69/p/5581484.html