多 项 式 全 家 桶(便携式)

多项式卷积

F×G 表示 FG 的卷积。暂时略去 FFT 与 NTT 的推导过程(应该……会补的吧?)。时间复杂度 O(nlogn)

多项式牛顿迭代

给定 F。求 G 使 FG0(modxn)。这里 FG(x)=F(G(x))

考虑倍增。n=1 时单独求解。假设已经得到了 modm1=xn2 的解 G1,考虑 modm=xn

FG1(x) 处进行泰勒展开,有

i0F(i)(G1(x))i!(G(x)G1(x))i0(modxn)

因为 G(x)G1(x) 的最低非零项次数为 n2,所以 i2 时的项恒为 0。因此

F(G1(x))+F(G1(x))(G(x)G1(x))0(modxn)G(x)=G1(x)F(G1(x))F(G1(x))(modxn)

一般来说不会直接用到,但是可以用它直接解决下面的几个问题。

多项式乘法逆

给定 F,求 G 使 F×G1(modxn)G 可以记作 1FF1

应用牛顿迭代。方程是 1GF=0。有 G=G1G11FG12=2G1G12F=G1(2G1F)。时间复杂度 T(n)=T(n2)+O(nlogn)=O(nlogn)

多项式带余除法

给定 F,G,求 Q,R 满足 F=G×Q+R,degR<degG。可以证明 (Q,R) 是唯一存在的。

Fr 表示将 F 的系数序列翻转之后的多项式,Gr,Qr,Rr 同理。记 n=degF,m=degG。考虑将 1x 作为自变量代入式子,则得到 Fr=Gr×Gr+R×xnm+1。式子两边对 xnm+1 取模得到 QrFrGr(modxnm+1)。而 degQ=nm,所以直接用多项式乘法计算 FrGr 就可以得到 Qr。有了 Qr 自然有 Q,那么 R=FG×Q 即可。时间复杂度 O(nlogn)

多项式开方

给定 F,求 G 使 G2F(modxn)

应用牛顿迭代。有 G=G1G12F2G1=F+G122G1。时间复杂度 O(nlogn)

多项式求导 / 不定积分

太简单了。直接按照定义做就可以了。时间复杂度 O(n)

多项式 ln

给定 F,求 G 使 GlnF(modxn)。注意只有当 F(0)=1 时有解。

ln 没法处理,考虑两边求导。有 GFF(modxn)。于是 GFF(modxn)

多项式 exp

给定 F,求 G 使 GeF(modxn)。注意只有当 F(0)=0 时有解。

应用牛顿迭代。有 G=G1lnG1F1G1=G1(1lnG1+F)。时间复杂度 O(nlogn)

多项式快速幂

给定 F,k,求 Fkmodxn

快速幂使用倍增?大可不必。直接做 eklnF 即可。如果 F(0)1 则只要用 FF(0) 计算,最后乘上 F(0)k。时间复杂度 O(nlogn)。注意可能需要特判 F(0)=0 的情形。

其实直接使用倍增当然也是可以的,还能够处理任意的模多项式。

const int N=2e5+5,P=998244353;
int qpow(int a,int b=P-2){
int c=1;
for(;b;b>>=1,a=1ll*a*a%P)
if(b&1)c=1ll*c*a%P;
return c;
}
typedef vector<int> arr;
namespace poly{
const int _G=3,invG=::qpow(3);
int tr[N<<1];
void NTT(arr&f,int op){
int n=f.size();
for(int i=0;i<n;++i)
if(i<tr[i])swap(f[i],f[tr[i]]);
for(int p=2;p<=n;p<<=1){
int len=p>>1,tG=::qpow(op==1?_G:invG,(P-1)/p);
for(int k=0;k<n;k+=p){
int buf=1;
for(int l=k;l<k+len;++l){
int tmp=1ll*buf*f[len+l]%P;
f[len+l]=(f[l]-tmp+P)%P;f[l]=(f[l]+tmp)%P;
buf=1ll*buf*tG%P;
}
}
}
if(op==-1){
int inv=::qpow(n);
for(int i=0;i<n;++i)f[i]=1ll*f[i]*inv%P;
}
}//NTT模板
arr times(arr f,arr g){
int n=f.size(),m=g.size();
for(m+=n,n=1;n<=m;n<<=1);
f.resize(n);g.resize(n);
for(int i=0;i<n;++i)
tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
NTT(f,1);NTT(g,1);
for(int i=0;i<n;++i)f[i]=1ll*f[i]*g[i]%P;
NTT(f,-1);return f;
}//多项式卷积
arr inv(int n,arr f){
if(n==1)return {::qpow(f[0])};
arr g=inv((n+1)/2,f);f.resize(n);
f=times(f,g);for(int i=0;i<n;i++)f[i]=P-f[i];f[0]+=2;
f.resize(n);f=times(f,g);f.resize(n);return f;
}//多项式求逆
arr sqrt(int n,arr f){
if(n==1)return {1};//这里只考虑f[0]=1的情况。实际上可能要求二次剩余。
arr g1=sqrt((n+1)/2,f);f.resize(n);
arr g=times(g1,g1);g.resize(n);int inv2=(P+1)/2;
for(int i=0;i<n;i++)g[i]=1ll*(f[i]+g[i])%P*inv2%P;
g1.resize(n);g1=inv(n,g1);g=times(g,g1);g.resize(n);
return g;
}//多项式开根
pair<arr,arr> mod(arr f,arr g){
int n=f.size(),m=g.size();arr ff=f,gg=g;
reverse(ff.begin(),ff.end());
reverse(gg.begin(),gg.end());gg.resize(n+1);
arr q=times(ff,inv(n+1,gg));
q.resize(n-m+1);reverse(q.begin(),q.end());
arr r=times(g,q);r.resize(m-1);
for(int i=0;i<m-1;i++)r[i]=(f[i]-r[i]+P)%P;
return make_pair(q,r);
}//多项式带余除法
arr ln(arr f){
int n=f.size();arr f1;f1.resize(n);
for(int i=1;i<n;i++)f1[i-1]=1ll*f[i]*i%P;
f=inv(n,f);f1=times(f1,f);f1.resize(n);
f[0]=0;for(int i=1;i<n;i++)f[i]=1ll*f1[i-1]*::qpow(i)%P;
return f;
}//多项式 ln
arr exp(int n,arr f){
if(n==1)return {1};
arr g1=exp((n+1)/2,f);f.resize(n);g1.resize(n);
arr g=ln(g1);for(int i=0;i<n;i++)g[i]=(f[i]-g[i]+P)%P;++g[0];
g=times(g,g1);g.resize(n);return g;
}//多项式 exp
arr qpow(arr f,int k1,int k2){
//因为k可能很大,所以传入k1=k%P,k2=k%(P-1)
int n=f.size(),x=f[0],invx=::qpow(x);
for(int i=0;i<n;i++)f[i]=1ll*f[i]*invx%P;
f=ln(f);for(int i=0;i<n;i++)f[i]=1ll*f[i]*k1%P;
f=exp(n,f);x=::qpow(x,k2);
for(int i=0;i<n;i++)f[i]=1ll*f[i]*x%P;
return f;
}//多项式快速幂
}
posted @   by_chance  阅读(131)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示