斯特林数,上升幂,下降幂学习笔记
斯特林数,上升幂,下降幂,普通幂的定义
第二类斯特林数
n | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||
1 | ||||||||||
2 | ||||||||||
3 | ||||||||||
4 | ||||||||||
5 | ||||||||||
6 | ||||||||||
7 | ||||||||||
8 | ||||||||||
9 |
定义
有
第一类斯特林数
n | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||
1 | ||||||||||
2 | ||||||||||
3 | ||||||||||
4 | ||||||||||
5 | ||||||||||
6 | ||||||||||
7 | ||||||||||
8 | ||||||||||
9 |
定义
有
上升幂,下降幂,普通幂
斯特林数的性质
整理一下
表示普通幂时用第二类斯特林数,表示上升幂、下降幂时用第一类斯特林数,用大数表示小数时用
考虑公式的嵌套,让斯特林数更毒瘤:
既然有二项式反演,也有斯特林反演!
证明略。
在来个柿子:
考虑组合意义来解释,
二项式反演可得:
可以发现这是个卷积,
例题
遇到不可以直接计算的多项式、组合数计数题时可以考虑普通幂转下降幂或上升幂。
其中
有
第二类斯特林数直接暴力
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 3001;
ll stirling[N][N], MOD;
void get_stirling(ll tt){
stirling[0][0]=1;
for(ll i = 1; i <= tt; i++)
for(ll j = 1; j <= i; j++)
stirling[i][j] = 1ll*(1ll*stirling[i-1][j]*j%MOD+1ll*stirling[i-1][j-1]) % MOD;
}
ll my_pow(ll x, ll y){
ll res = 1;
for(;y;y>>=1,x=x*x%MOD)
if(y&1)res=res*x%MOD;
return res;
}
ll n, X, m;
ll a[N], b[N];
int main(){
cin>>n>>X>>MOD>>m;
get_stirling(m);
for(ll i = 0; i <= m; i++)
cin>>a[i];
for(ll i = 0; i <= m; i++)
for(ll j = i; j <= m; j++)
b[i] = (b[i]+stirling[j][i]*a[j]%MOD)%MOD;
ll ans = 0;
for(ll i = 0, now = 1; i <= m; i++){
ll res = b[i] * now % MOD * my_pow(X, i) % MOD * my_pow(X+1, n-i) % MOD;
ans = (ans + res) % MOD;
now = now * (n-i) % MOD;
}
cout<<(ans+MOD)%MOD<<endl;
return 0;
}
考虑组合意义来解释,
二项式反演可得:
可以发现这是个卷积,
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 167772161;
const ll N = 1e6 + 11;
ll rev[N], w[N], gen = 3;
ll my_pow(ll x, ll y){
ll res = 1;
for(;y;y>>=1,x=x*x%MOD)
if(y&1)res=res*x%MOD;
return res;
}
void NTT(ll *a, ll Len, bool type){
for(int i = 0; i < Len; i++){
rev[i] = (rev[i>>1]>>1) + (i&1?Len>>1:0);
if(rev[i]>i)swap(a[rev[i]], a[i]);
}
for(ll d = 1; d < Len; d <<= 1){
ll W = my_pow(gen, (MOD-1)/(d*2));
if(type) W = my_pow(W, MOD-2);
w[0] = 1;
for(ll i = 1; i < d; i++)
w[i] = w[i-1] * W % MOD;
for(ll fir = 0; fir < Len; fir += (d*2)){
ll sec = fir + d;
for(ll i = 0 ; i < d; i++){
ll a0 = a[fir+i], a1 = a[sec+i]*w[i]%MOD;
a[fir+i] = (a0+a1)%MOD;
a[sec+i] = (a0-a1+MOD)%MOD;
}
}
}
if(type){
ll invlen=my_pow(Len, MOD-2);
for(int i=0; i<Len; i++)
a[i] = a[i]*invlen%MOD;
}
}
ll f[N], g[N], fac[N], ifac[N], n;
ll fu(int k){return k&1?-1:1;}
int main(){
cin>>n;
ifac[0]=fac[0]=1;
for(ll i = 1; i <= n; i++) fac[i] = fac[i-1] * i % MOD;
ifac[n] = my_pow(fac[n], MOD-2);
for(ll i = n-1; i; i--) ifac[i] = ifac[i+1] * (i+1) % MOD;
for(ll i = 0; i <= n; i++){
f[i] = fu(i)*ifac[i]%MOD, f[i] = (f[i]+MOD)%MOD;
g[i] = my_pow(i, n) * ifac[i] % MOD;
// cout << f[i] << " " << g[i] << endl;
}
ll len = 1;
while(len<=(n*2))len<<=1;
NTT(f, len, 0), NTT(g, len, 0);
for(int i = 0; i < len; i++)
f[i] = f[i] * g[i] % MOD, f[i] = (f[i] + MOD) % MOD;
NTT(f, len, 1);
for(int i = 0; i <= n; i++)
cout << f[i] << " ";
cout << endl;
return 0;
}
有
考虑倍增:
假设我们求出了
现在要求
有:
设
可以发现里面是个卷积,
注意卷积时
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 167772161;
const ll N = 7e5, gen=3;
ll my_pow(ll x, ll y){
ll res = 1;
for(;y;y>>=1,x=x*x%MOD)
if(y&1)res=res*x%MOD;
return res;
}
ll inv(ll x){
return my_pow(x, MOD-2);
}
ll rev[N], w[N];
void ntt(ll *a, ll Len, bool type){
for(ll i = 0 ; i < Len; i++){
rev[i] = (rev[i>>1]>>1) + (i&1?Len>>1:0);
if(rev[i]>i)swap(a[rev[i]], a[i]);
}
for(ll d=1; d<Len; d<<=1){
ll W = my_pow(gen, (MOD-1)/(d*2));
if(type) W = inv(W);
w[0]=1;
for(ll i = 1; i < d; i++)
w[i] = w[i-1] * W % MOD;
for(ll fir=0; fir<Len; fir+=d*2){
ll sec=fir+d;
for(ll i=0; i<d; i++){
ll a0=a[fir+i], a1=a[sec+i]*w[i]%MOD;
a[fir+i]=(a0+a1)%MOD;
a[sec+i]=(a0-a1+MOD)%MOD;
}
}
}
if(type){
ll invlen=inv(Len);
for(ll i=0; i<Len; i++)
a[i]=a[i]*invlen%MOD;
}
}
ll aa[N], bb[N];
void mul(ll *a, ll *b, ll alen, ll blen){
ll len=1;
while(len<=(alen+blen))len<<=1;//不能多也不能少!(len=xlen+ylen)
ntt(a, len, 0);
ntt(b, len, 0);
for(int i=0; i<len; i++)
a[i]=a[i]*b[i]%MOD;
ntt(a, len, 1);
}
ll ans[N], s[N], A[N], B[N], C[N];
ll fac[N], ifac[N];
void sol_striling_one_line(ll tt){
if(tt==1){s[1]=1;return;}
if(tt&1){
sol_striling_one_line(tt-1);
for(ll i=tt; i; i--)
s[i] = (s[i-1] + s[i]*(tt-1)%MOD)%MOD;
s[0]=s[0]*(tt-1)%MOD;
return;
}
ll slen = tt/2, res=1;
sol_striling_one_line(slen);
for(int i=0; i<=slen; i++)
A[i]=s[i]*fac[i]%MOD,
B[i]=res*ifac[i]%MOD,
res=res*slen%MOD;
reverse(A, A+slen+1);
mul(A, B, slen+1, slen+1);
for(int i=0; i<=slen; i++)
C[i]=A[slen-i]*ifac[i]%MOD;
mul(s,C,slen+1,slen+1);
int len=1;
while(len<=(tt+2))len<<=1;
for(int i=slen+1; i<len; i++)A[i]=B[i]=C[i]=0;
for(int i=tt+1; i<len; i++)s[i]=0;
}
void init_fac(int tt){
fac[0]=ifac[0]=1;
for(ll i=1; i<=tt; i++)
fac[i]=fac[i-1]*i%MOD;
ifac[tt]=my_pow(fac[tt], MOD-2);
for(ll i=tt-1; i; i--)
ifac[i] = ifac[i+1]*(i+1)%MOD;
}
ll* striling_first_line(ll tt){
init_fac(tt<<1);
memset(A, 0, sizeof A);
memset(B, 0, sizeof B);
memset(s, 0, sizeof s);
memset(C, 0, sizeof C);
sol_striling_one_line(tt);
return s;
}
int main(){
ll n;
cin>>n;
ll* striling=striling_first_line(n);
for(int i=0; i<=n; i++) cout<<striling[i]<<' ';
cout<<'\n';
return 0;
}
本文作者:Fzrcy's Blog
本文链接:https://www.cnblogs.com/fzrcy/p/17323656.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步