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