多项式全家桶
这里记录了我个人的多项式学习过程。
(可能在任何时候改成vector
写法的代码,但不知道是什么时候)
下列过程中默认
FFT
发现多项式
在不好用系数去维护多项式的时候,考虑使用点值维护。
取
实现代码(这个是作者的古早码风,请见谅):
void DFT(Comp *f,int n,int rev)
{
if(n==1) return;
for(int i=0;i<n;i++) temp[i]=f[i];
for(int i=0;i<n;i++)
if(i&1) f[(i>>1)+hf]=temp[i];
else f[i>>1]=temp[i];
Comp *g=f,*h=f+hf;
DFT(g,hf,rev);DFT(h,hf,rev);
Comp cur,step;
cur.img=0,cur.real=1;
step.img=sin(2*M_PI*rev/n),step.real=cos(2*M_PI/n);
for(int i=0;i<hf;i++)
{
temp[i]=g[i]+cur*h[i];
temp[i+hf]=g[i]-cur*h[i];
cur=cur*step;
}
for(int i=0;i<n;i++) f[i]=temp[i];
return;
}
NTT
使用 double
和复数精度较低,同时有的题目需要对某些数取模,考虑单位根在模意义下的替代品——原根。
假设质数
常见的
一般使用蝴蝶变换和非递归写法,时间复杂度
void ntt(long long *num,int len,bool typ)
{
long long g,w,x,y;
for(int i=1;i<len;i++)
{
rev[i]=rev[i>>1]>>1;
if(i&1) rev[i]|=len>>1;
}
for(int i=0;i<len;i++) if(rev[i]<i) swap(num[rev[i]],num[i]);
for(int i=1;i<len;i<<=1)
{
if(typ) w=qpow(332748118,(Mod-1)/(i<<1));
else w=qpow(3,(Mod-1)/(i<<1));
for(int j=0;j<len;j+=(i<<1))
{
g=1;
for(int k=0;k<i;k++)
{
x=num[j+k],y=g*num[j+i+k]%Mod;
num[j+k]=(x+y)%Mod;
num[j+i+k]=(x+Mod-y)%Mod;
g=g*w%Mod;
}
}
}
if(typ)
{
long long ny=qpow(len,Mod-2);
for(int i=0;i<len;i++) num[i]=num[i]*ny%Mod;
}
return;
}
多项式乘法逆
对于
考虑递归处理。
假设我们已知
所以
两侧同时取平方
展开得
两侧同时乘上
即
又因为在
时间复杂度
实现代码:
void polyinv(long long *F,long long *G,int lenth)//G<-1/F
{
G[0]=qpow(F[0],Mod-2);
int len;
for(len=2;len<(lenth<<1);len<<=1)
{
for(int i=0;i<len;i++) f[i]=F[i],g[i]=G[i];
for(int i=len;i<(len<<1);i++) f[i]=g[i]=0;
ntt(f,len<<1,false),ntt(g,len<<1,false);
for(int i=0;i<(len<<1);i++) f[i]=f[i]*g[i]%Mod;
ntt(f,len<<1,true);
for(int i=len;i<(len<<1);i++) f[i]=0;
for(int i=0;i<len;i++) f[i]=f[i]?Mod-f[i]:0;
f[0]=chk(f[0]+2);
ntt(f,len<<1,false);
for(int i=0;i<(len<<1);i++) f[i]=f[i]*g[i]%Mod;
ntt(f,len<<1,true);
for(int i=0;i<len;i++) G[i]=f[i];
}
for(int i=lenth+1;i<len;i++) G[i]=0;
return;
}
多项式除法/取模
对于
对于一个多项式
不难发现,
由于
则
两侧同时乘上
所以
所以
由于
求出
时间复杂度
代码实现:
void polymod(long long *F,int n,long long *G,int m,long long *Q,long long *R)//F=Q*G+R
{
int len=0;
reverse(F,F+n+1);
reverse(G,G+m+1);
polyinv(G,tr,n-m+1);
for(len=1;len<=2*n-m;len<<=1);
ntt(F,len,false),ntt(tr,len,false);
for(int i=0;i<len;i++) tr[i]=tr[i]*F[i]%Mod;
ntt(F,len,true),ntt(tr,len,true);
for(int i=0;i<=n-m;i++) Q[i]=tr[i];
reverse(Q,Q+n-m+1);
reverse(G,G+m+1);
reverse(F,F+n+1);
for(len=1;len<=n;len<<=1);
ntt(G,len,false),ntt(Q,len,false);
for(int i=0;i<len;i++) tr[i]=G[i]*Q[i]%Mod;
ntt(Q,len,true),ntt(tr,len,true);
for(int i=0;i<m;i++) R[i]=chk(F[i]+Mod-tr[i]);
return;
}
多项式多点求值
已知
是由秦九韶算法求显然是没有前途的,所以我们考虑其他能够求多项式点值的方法。
我们知道,将
在上式中带入
但是求一次多项式取模是
对于问题
(然而常数爆炸的我只在洛谷上过了一个点……)
转置原理处理多项式多点求值
这种时候我们就需要一种更快的方法。<---这是个链接。
多项式求ln
对于
考虑求导和积分:
这也就意味着,我们只要求出在
然后再把我们求出来的东西积分回去,就得到了答案
时间复杂度
代码实现:
void polyder(long long *G,long long *F,int len)
{
for(int i=1;i<len;i++)
G[i-1]=i*F[i]%Mod;
G[len-1]=0;
return;
}
long long F[LIM],G[LIM],H[LIM];
int n,len;
int main()
{
n=Qread();
for(int i=0;i<n;i++)
F[i]=Qread();
for(len=1;len<n;len<<=1);
polyinv(G,F,len);
polyder(H,F,len);
ntt(G,len<<1,false),ntt(H,len<<1,false);
for(int i=0;i<(len<<1);i++) G[i]=G[i]*H[i]%Mod;
ntt(G,len<<1,true);
printf("0 ");
for(int i=1;i<n;i++)
printf("%lld ",G[i-1]*qpow(i,Mod-2)%Mod);
return 0;
}
多项式exp
对于
因为
两边取
移项得
记
所以对其求导得
考虑初始选择点
带入得
我们来尝试证明我们能够在
假设我们知道
由于
考虑
由于
这也就意味着,在泰勒展开式中,只有
所以
而我们需要求的是使得
则
时间复杂度
代码实现:
void polyexp(long long *F,long long *G,int lenth)
{
G[0]=1;
for(int len=2;len<(lenth<<1);len<<=1)
{
polyln(G,expg,len);
for(int i=0;i<len;i++) expg[i]=chk(F[i]+Mod-expg[i]);
expg[0]=chk(expg[0]+1);
for(int i=0;i<len;i++) exf[i]=G[i];
for(int i=len;i<(len<<1);i++) exf[i]=expg[i]=0;
ntt(exf,len<<1,false),ntt(expg,len<<1,false);
for(int i=0;i<(len<<1);i++) exf[i]=exf[i]*expg[i]%Mod;
ntt(exf,len<<1,true);
for(int i=0;i<len;i++) G[i]=exf[i];
}
return;
}
多项式快速幂(普通版)
对于
我们知道
时间复杂度
多项式开根
对于
法一:直接理解成
法二:考虑使用牛顿迭代,我们记
考虑
则
所以
常数小于法一,在洛谷上大数据平均需要296ms。
时间复杂度
法二代码实现:
long long sqrf[LIM],sqrg[LIM];
void polysqrt(long long *F,long long *G,int lenth)
{
G[0]=F[0];
for(int len=2;len<(lenth<<1);len<<=1)
{
memset(sqrf,0,sizeof(long long)*(len<<1));
memset(sqrg,0,sizeof(long long)*(len<<1));
polyinv(G,sqrg,len);
memcpy(sqrf,F,sizeof(long long)*len);
ntt(sqrf,len<<1,false),ntt(sqrg,len<<1,false);
for(int i=0;i<(len<<1);i++) sqrf[i]=sqrf[i]*sqrg[i]%Mod;
ntt(sqrf,len<<1,true);
for(int i=0;i<=len;i++) G[i]=inv2*(G[i]+sqrf[i])%Mod;
}
return;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具