@Scarlet
        
        2017-03-27T11:07:42.000000Z
        字数 12189
        阅读 8068
    多项式 数学
感觉自己听得风就是雨就下海了。
没有人不知道它是用来干什么的吧
两个多项式求
简单的说,我们可以考虑先在时间内把系数表示的转成点值表达,再用时间计算出的点值表达,最后用时间内把转换为系数表达。
系数表示转点值表示称为,点值表示转系数表示称为
满足的复数有个,分别为,其中称位主次单位根.
单位根有些很好的性质:
(为了方便进行和,下文中的均是不小于多项式次数界的最小的幂次)
先记计算在n次单位根处的取值。定义新多项式 
开心地发现: 
亦即 
转化问题: 
在的值,变为在的值. 
次数减半,递归硬艹。
实质上就是一个矩阵乘法 
关键是找到左边矩阵的逆矩阵。可以构造出来:每个元素取共轭复数,再除以。正确性易证。
到此我们已经可以写出一个常数较大的递归FFT了。
观察到我们的分治是将奇偶分类,即对于每一层我取这层对应位数的0放在一起,1放在一起。
那么最后元素所处的位置,就是将的二进制表示翻转的位置。 
引入算法的图:

/*Author:Scarlet*/#define pi M_PItypedef complex<double>C;int N,g[maxn];void DFT(C *a,int f){for(int i=0;i<N;i++)if(g[i]>i)swap(a[i],a[g[i]]);for(int i=1;i<N;i<<=1){C e(cos(pi/i),f*sin(pi/i));for(int j=0;j<N;j+=i<<1){C w(1,0);for(int k=0;k<i;k++,w*=e){C x=a[j+k],y=w*a[j+k+i];a[j+k]=x+y;a[j+k+i]=x-y;}}}if(f-1)for(int i=0;i<N;i++)a[i]/=N;}void mul(C *a,C *b,int n){int t=-1;for(N=1;N<=n;N<<=1,t++);for(int i=1;i<N;i++)g[i]=(g[i>>1]>>1)|((i&1)<<t);DFT(a,1);DFT(b,1);for(int i=0;i<N;i++)a[i]=a[i]*b[i];DFT(a,-1);}
复数计算常数大,误差大,可以做到多项式乘法的过程中取模。
当模数十分特殊时可以用数论变换加速,基于一个极强的等价: 
其中是的原根。 
条件是,其中
/*Author:Scarlet*/void NTT(LL *a,int f){for(int i=0;i<N;i++)if(g[i]>i)swap(a[i],a[g[i]]);for(int b=0,i=1;i<N;i<<=1){b++;LL e=Pow(3,(1<<23-b)*119);if(f<0)e=Pow(e,mod-2);for(int j=0;j<N;j+=i<<1){LL w=1;for(int k=0;k<i;k++,w=w*e%mod){LL x=a[j+k],y=w*a[j+k+i]%mod;a[j+k]=x+y;a[j+k+i]=x-y;if(a[j+k+i]<0)a[j+k+i]+=mod;if(a[j+k]>=mod)a[j+k]-=mod;}}}if(f<0){LL v=Pow(N,mod-2);for(int i=0;i<N;i++)a[i]=a[i]*v%mod;}}
选自CodeForces Submission #19950343
该说啥我也不知道辣
给出多项式,求多项式使得 
这里我们将使用一种倍增的思想来完成。 
首先,当时, . 
假如我们已经求出了在模 意义下的答案 ,要求在 意义下的答案。那么: 
由于 逆元存在,故得: 
那么我们将等式各部分平方,展开后得: 
两边同乘,利用逆元的性质移项便可得: 
上式便可在时间内求出了。 
由于每次是倍增的,所以总复杂度可得为 .
多项式除法...
给出多项式,求出多项式,满足: 
其中:表示的最高次项的次数。
不妨令: 
假如我们可以把多项式放到模意义下,那很多操作都会很好做了。 
这样,我们就要排除掉的影响。 
怎么做呢?定义: 
考虑下这个变换的形式理解,即将多项式的系数翻转,高次项的到低次项来,低次项的到高次项去。 
那么不妨将最初的等式两边的替换为,再都乘以,看能得到什么。 
放至模意义下!就消去了带来的影响! 
注意到,故不会受到模意义的影响! 
我们可以利用多项式求逆算出,用FFT算出 ,然后代入最开始的式子中,就可以解得了。 
注意到由于的项必然不为0,故 的第0项必然不为0。则逆元一定存在。 
复杂度:
Picks好神啊
给出多项式.求. 
显然. 注意这里的开方可以开出负数。 
考虑如何从推到. 
注意到实际上的末位与一致。故可以同时维护 
与多项式求逆的复杂度分析一致,此时我们的复杂度依然是
前置技能讲完了是不是讲模型了啊
 
好像和多项式乘法没什么区别啊...
 
构造,指标变换就变回卷积了 
 
其中
构造,自乘次,快速幂。
XJB找个原根对的每个下标取指标,然后同上
构造,然后和卷积,所得答案要除以 
咦,好像就是反卷积嘛..
类比与欧几里德算法,我们可以证明欧几里德算法需要的结论在多项式上均成立。 
考虑使用欧几里德算法。 
每次相当于求两个多项式的商与余数。 
正好可以利用多项式除法解决。 
不过每次取模均只能将最高次减小1。 
复杂度
类比扩展欧几里得
和之前一样,构造多项式:  
 
 
 
则:  
 
 
所以如果能被有限项多项式表示的话就做完了。。。 
构造 
容易发现这个多项式就是自身的卷积,所以我们直接把这个多项式自乘一下  
 
解得 
模板!
/*Author:Scarlet*/#include<bits/stdc++.h>#define maxn 262144using namespace std;typedef long long LL;#define G c=getchar()inline int read(){int x=0,f=1;char G;while(c>57||c<48){if(c=='-')f=-1;G;}while(c>47&&c<58){x=x*10+c-48;G;}return x*f;}#define pi M_PItypedef complex<double>C;int N,g[maxn];C a[maxn],b[maxn];void DFT(C *a,int f){for(int i=0;i<N;i++)if(g[i]>i)swap(a[i],a[g[i]]);for(int i=1;i<N;i<<=1){C e(cos(pi/i),f*sin(pi/i));for(int j=0;j<N;j+=i<<1){C w(1,0);for(int k=0;k<i;k++,w*=e){C x=a[j+k],y=w*a[j+k+i];a[j+k]=x+y;a[j+k+i]=x-y;}}}if(f-1)for(int i=0;i<N;i++)a[i]/=N;}void mul(C *a,C *b,int n){int t=-1;for(N=1;N<=n;N<<=1,t++);for(int i=1;i<N;i++)g[i]=(g[i>>1]>>1)|((i&1)<<t);DFT(a,1);DFT(b,1);for(int i=0;i<N;i++)a[i]=a[i]*b[i];DFT(a,-1);}int main(){int n=read(),m=read();for(int i=0;i<=n;i++)a[i]=read();for(int i=0;i<=m;i++)b[i]=read();mul(a,b,n+m+1);for(int i=0;i<=n+m;i++)printf("%d ",(int)(a[i].real()+0.5));}
感觉挺傻逼的啊... 
 
可以拆成左右两边算,发现左边就是一个卷积,右边则是一个反卷积。 
不就做完了?
/*Author:Scarlet*/#include<bits/stdc++.h>#define maxn 262144using namespace std;typedef long long LL;#define G c=getchar()inline int read(){int x=0,f=1;char G;while(c>57||c<48){if(c=='-')f=-1;G;}while(c>47&&c<58){x=x*10+c-48;G;}return x*f;}typedef complex<long double> C;#define pi M_PIint N,g[maxn];C q[maxn],qq[maxn],f1[maxn],f2[maxn];void DFT(C *a,int f){for(int i=0;i<N;i++)if(g[i]<i)swap(a[i],a[g[i]]);for(int i=1;i<N;i<<=1){C e(cos(pi/i),f*sin(pi/i));for(int j=0;j<N;j+=i<<1){C w(1,0);for(int k=0;k<i;k++,w*=e){C x=a[j+k],y=w*a[j+k+i];a[j+k]=x+y,a[j+k+i]=x-y;}}}if(f-1)for(int i=0;i<N;i++)a[i]/=N;}void mul(C *a,C *b,int n){int t=-1;for(N=1;N<=n;N<<=1,t++);for(int i=1;i<N;i++)g[i]=g[i>>1]>>1|((i&1)<<t);DFT(a,1);DFT(b,1);for(int i=0;i<N;i++)a[i]*=b[i];DFT(a,-1);}int main(){int n=read();double _;for(int i=0;i<n;i++)scanf("%lf",&_),qq[i]=q[i]=_;for(int i=1;i<n;i++)f1[i]=1.0/i/i;mul(f1,q,n+n);for(int i=0;i<n-1;i++)f2[i]=1.0/(n-1-i)/(n-1-i);mul(f2,qq,n+n);for(int i=0;i<n;i++)printf("%.10lf\n",(double)(f1[i].real()-f2[n-1+i].real()));return 0;}
多DFT了一次,有点慢...
Manacher求一遍回文子串的数量 
可以用数对和模型求一遍回文子序列 
答案就是两个相减
/*Author:Scarlet*/#include<bits/stdc++.h>#define maxn 524288#define mod 1000000007using namespace std;typedef long long LL;#define G c=getchar()inline int read(){int x=0,f=1;char G;while(c>57||c<48){if(c=='-')f=-1;G;}while(c>47&&c<58){x=x*10+c-48;G;}return x*f;}#define pi M_PIint g[maxn],N;typedef complex<double> C;C b[maxn],a[maxn];char s[maxn];void DFT(C *a,int f){for(int i=0;i<N;i++)if(g[i]<i)swap(a[i],a[g[i]]);for(int i=1;i<N;i<<=1){C e(cos(pi/i),f*sin(pi/i));for(int j=0;j<N;j+=i<<1){C w(1,0);for(int k=0;k<i;k++,w*=e){C x=a[j+k],y=w*a[j+k+i];a[j+k]=x+y,a[j+k+i]=x-y;}}}if(f-1)for(int i=0;i<N;i++)a[i]/=N;}int manacher(char str[],int n){static char s[maxn];static int f[maxn];s[0]='$',s[1]='#';for(int i=1;i<=n;i++)s[i<<1]=str[i],s[i<<1|1]='#';n=n<<1|1;int mx=1,id=1,re=0;for(int i=1;i<=n;i++){f[i]=min(f[id*2-i],mx-i);for(;s[i+f[i]]==s[i-f[i]];f[i]++);if(f[i]+i>mx)mx=f[i]+i,id=i;re+=f[i]>>1,re%=mod;}return re;}LL gg[maxn];int P[maxn];int main(){P[0]=1;for(int i=1;i<maxn;i++)P[i]=(P[i-1]<<1)%mod;scanf("%s",s+1);int n=strlen(s+1);int ans=-manacher(s,n),t=-1;for(N=1;N<=n*2;N<<=1)t++;for(int i=1;i<N;i++)g[i]=g[i>>1]>>1|((i&1)<<t);for(int i=1;i<=n;i++)a[i]=(int)(s[i]=='a');DFT(a,1);for(int i=0;i<N;i++)b[i]=a[i]*a[i];memset(a,0,sizeof(a));for(int i=1;i<=n;i++)a[i]=(int)(s[i]=='b');DFT(a,1);for(int i=0;i<N;i++)b[i]+=a[i]*a[i];DFT(b,-1);for(int i=1;i<N;i++)gg[i]+=LL(b[i].real()+0.5);for(int i=1;i<N;i++)(ans+=P[gg[i]+1>>1]-1)%=mod;printf("%d",ans<0?mod+ans:ans);return 0;}
辣鸡指标变换
/*Author:Scarlet*/#include<bits/stdc++.h>#define maxn 16384#define mod 1004535809using namespace std;typedef long long LL;#define _ c=getchar()inline LL read(){LL x=0,f=1;char _;while(c>57||c<48){if(c=='-')f=-1;_;}while(c>47&&c<58){x=x*10+c-48;_;}return x*f;}int n,m,x,S,G,N,g[maxn];LL Pow(LL x,LL y){LL a=1;for(x%=mod;y;y>>=1,x=x*x%mod)if(y&1)a=a*x%mod;return a;}bool check(int p,int m){int w=p;for(int i=1;i<m-2;i++,(w*=p)%=m)if(w==1)return 0;return 1;}int gpr(int m){for(int i=2;;i++)if(check(i,m))return i;}void NTT(LL *a,int f){int b=0;for(int i=0;i<N;i++)if(g[i]>i)swap(a[i],a[g[i]]);for(int i=1;i<N;i<<=1){b++;LL e=Pow(3,(1<<21-b)*479);if(f<0)e=Pow(e,mod-2);for(int j=0;j<N;j+=i<<1){LL w=1;for(int k=0;k<i;k++,w=w*e%mod){LL x=a[j+k],y=w*a[j+k+i]%mod;a[j+k]=x+y;a[j+k+i]=x-y;if(a[j+k]>=mod)a[j+k]-=mod;if(a[j+k+i]<0)a[j+k+i]+=mod;}}}if(f<0){LL v=Pow(N,mod-2);for(int i=0;i<N;i++)a[i]=a[i]*v%mod;}}LL c[maxn];void mul(LL *a,LL *b){if(a==b){NTT(a,1);for(int i=0;i<N;i++)a[i]=a[i]*a[i]%mod;NTT(a,-1);for(int i=0;i<m-1;i++)(a[i]+=a[i+m-1])%=mod,a[i+m-1]=0;return;}memcpy(c,b,sizeof(c));NTT(a,1);NTT(b,1);for(int i=0;i<N;i++)a[i]=a[i]*b[i]%mod;NTT(a,-1);for(int i=0;i<m-1;i++)(a[i]+=a[i+m-1])%=mod,a[i+m-1]=0;memcpy(b,c,sizeof(c));}int A[maxn],B[maxn];LL a[maxn],b[maxn];int main(){n=read(),m=read(),x=read(),S=read();int t=-1;for(N=1;N<=2*m;N<<=1)t++;for(int i=1;i<N;i++)g[i]=g[i>>1]>>1|((i&1)<<t);G=gpr(m);for(LL i=0,w=1;i<m-1;i++,w=w*G%m)B[i]=w;for(int i=0;i<m-1;i++)A[B[i]]=i;for(int i=1;i<=S;i++){int x=read();if(x)b[A[x]]++;}a[0]=1;for(;n;n>>=1,mul(b,b))if(n&1)mul(a,b);printf("%lld",a[A[x]]);return 0;}
说是References实际上是Copies&Rearrangements啦
[1]Picks.Fast Fourier Transform[EB/OL].logdown,2014 
[2]Picks.Inverse Element of Polynomial[EB/OL].logdown,2014 
[3]Picks.Polynomial Division[EB/OL].logdown,2014 
[4]Picks.Square Root of Polynomial[EB/OL].logdown,2014 
[5]PoPoQQQ.一些常见数列的生成函数推导[EB/OL].csdn,2016