斯特林数和下降幂学习笔记
定义
第二类斯特林数
第二类斯特林数的递推式:
第一类斯特林数的递推式:
第二类斯特林数 行
用容斥原理和二项式反演可以证明:
可以用卷积计算,
for(i=0;i<=n;++i) f[i]=kuai(i,n)*finv[i]%moder;
for(i=0;i<=n;++i) g[i]=i&1?sub(0,finv[i]):finv[i];
getlim(n<<1),ntt(f,1),ntt(g,1);
for(i=0;i<lim;++i) h[i]=f[i]*g[i]%moder;
ntt(h,-1);
for(i=0;i<=n;++i) printf("%lld ",h[i]); printf("\n");
第二类斯特林数 列
一个集合装
求它的
因为是 EGF,所以要乘上
先
for(i=0;i<n;++i) f[i]=finv[i+1];
Ln(f,lnf,n);
for(i=0;i<n;++i) lnf[i]=lnf[i]*K%moder;
Exp(lnf,explnf,n);
for(i=0;i<K;++i) f[i]=0;
for(i=K;i<=n;++i) f[i]=explnf[i-K];
for(i=0;i<=n;++i) f[i]=f[i]*finv[K]%moder*fact[i]%moder;
for(i=0;i<=n;++i) printf("%lld ",f[i]); printf("\n");
第一类斯特林数 列
类似的,列出一个轮换的 EGF:
这里乘以
同样求
for(i=0;i<n;++i) f[i]=finv[i+1]*fact[i]%moder;
Ln(f,lnf,n);
for(i=0;i<n;++i) lnf[i]=lnf[i]*K%moder;
Exp(lnf,explnf,n);
for(i=0;i<K;++i) f[i]=0;
for(i=K;i<=n;++i) f[i]=explnf[i-K];
for(i=0;i<=n;++i) f[i]=f[i]*finv[K]%moder*fact[i]%moder;
for(i=0;i<=n;++i) printf("%lld ",f[i]); printf("\n");
第一类斯特林数 行
感觉是最难写的,也是 OI-Wiki 这四种中唯一一个没给代码的。
根据公式,构造同一行第一类斯特林数的生成函数:
考虑倍增,
是一个差卷积的形式,时间复杂度是
void solve(int a[],int n) {
if(n==0) return a[0]=1,void();
static int f[N]={},g[N]={};
solve(f,n>>1); int i,nn=n>>1;
for(i=0;i<=nn;++i) f[i]=(ll)a[i]*fact[i]%moder;
g[0]=1; for(i=1;i<=nn;++i) g[i]=(ll)g[i-1]*nn%moder;
MulT(f,g,nn+1,nn+1);
for(i=0;i<=nn;++i) f[i]=(ll)f[i]*finv[i]%moder;
Mul(a,f,nn+1,nn+1);
if(n&1) {
for(i=n;i;--i) f[i]=(f[i-1]+(ll)f[i]*n)%moder;
f[0]=(ll)f[0]*n%moder;
}
return ;
}
下降幂多项式乘法
和普通多项式乘法一样,考虑求出下降幂多项式的点值表示法。
先考虑一项,即
从这个式子可以看出,把下降幂多项式转换为点值表示法的 EGF,相当于和
把两个乘数的点值表示法的 EGF 按位相乘,再乘上
把点值表示法的 EGF 变回下降幂多项式也是容易的,和
时间复杂度是
下面代码每调用一次 DFT 都可能会让
void DFT(int f[],int n,int type) {
static int e[N]={}; int i;
getlim(n<<1);
for(i=n;i<lim;++i) f[i]=e[i]=0;
for(i=0;i<n;++i) e[i]=type==-1&&(i&1)?sub(0,finv[i]):finv[i];
ntt(f,1),ntt(e,1);
for(i=0;i<lim;++i) f[i]=(ll)f[i]*e[i]%moder;
ntt(f,-1); for(i=n;i<lim;++i) f[i]=0;
return ;
}
下降幂多项式转普通多项式
受第一类斯特林数行的影响,想到了分治,推了一天总算把式子推出来了。
现在要把下降幂多项式
考虑分治,假设现在只考虑
分治求的是只考虑区间内的项,转换后的普通多项式,以及
注意分治的时候是把
设左区间的答案为
在第一类斯特林数行中,已经知道了如何根据
得到
时间复杂度是
poly xAdd(poly f,int k) {
static poly a,b;
int n=f.size(),i,temp;
a.resize(n),b.resize(n);
for(i=0,temp=1;i<n;++i,temp=(ll)temp*k%moder)
a[i]=(ll)fact[i]*f[i]%moder,
b[i]=(ll)temp*finv[i]%moder;
f=Decmul(a,b);
for(i=0;i<n;++i) f[i]=(ll)f[i]*finv[i]%moder;
return f;
}
void solve(int l,int r,const poly &f,poly &g,poly &x) {
if(l==r) return g.resize(1),g[0]=f[l],x.resize(2),x[0]=0,x[1]=1,void();
poly L,xL,R,xR; int mid=l+r>>1;
solve(l,mid,f,L,xL),solve(mid+1,r,f,R,xR);
x=Mul(xL,xAdd(xR,sub(0,mid-l+1)));
g=Add(L,Mul(xAdd(R,sub(0,mid-l+1)),xL));
return ;
}
普通多项式转下降幂多项式
还是考虑分治,每次处理区间
设左区间
则
普通多项式和下降幂多项式相乘还是一样,求出点值表示法,
时间复杂度为
void dft(poly &f,int mod) {
int n=f.size(),i; poly g;
getlim(n<<1),f.resize(lim),g.resize(lim);
for(i=0;i<n;++i)
g[i]=mod&&(i&1)?sub(0,finv[i]):finv[i];
ntt(f,1),ntt(g,1);
for(i=0;i<lim;++i) f[i]=(ll)f[i]*g[i]%moder;
ntt(f,-1),f.resize(n);
return ;
}
void solve(int l,int r,const poly &f,poly &g) {
if(l==r) return g.resize(1),g[0]=f[l],void();
int mid=l+r>>1,i; poly L,R;
solve(l,mid,f,L),solve(mid+1,r,f,R);
poly x;
getlim(r-l+1),x.resize(lim),R.resize(lim);
dft(R,0),getlim(r-l+1);
for(i=0;i<lim;++i)
x[i]=kuai(i,mid-l+1);
for(i=0;i<lim;++i)
R[i]=(ll)R[i]*x[i]%moder;
dft(R,1),R.resize(r-l+1);
g=Add(L,R);
return ;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下