斯特林数,上升幂,下降幂学习笔记
斯特林数,上升幂,下降幂,普通幂的定义
第二类斯特林数
| n | \(n\brace 0\) | \(n\brace 1\) | \(n\brace 2\) | \(n\brace 3\) | \(n\brace 4\) | \(n\brace 5\) | \(n\brace 6\) | \(n\brace 7\) | \(n\brace 8\) | \(n\brace9\) |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 1 | \(0\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 2 | \(0\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 3 | \(0\) | \(1\) | \(3\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 4 | \(0\) | \(1\) | \(7\) | \(6\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 5 | \(0\) | \(1\) | \(15\) | \(25\) | \(10\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 6 | \(0\) | \(1\) | \(31\) | \(90\) | \(65\) | \(15\) | \(1\) | \(0\) | \(0\) | \(0\) |
| 7 | \(0\) | \(1\) | \(63\) | \(301\) | \(350\) | \(140\) | \(21\) | \(1\) | \(0\) | \(0\) |
| 8 | \(0\) | \(1\) | \(127\) | \(966\) | \(1701\) | \(1050\) | \(266\) | \(28\) | \(1\) | \(0\) |
| 9 | \(0\) | \(1\) | \(255\) | \(3025\) | \(7770\) | \(6951\) | \(2446\) | \(462\) | \(36\) | \(1\) |
定义 \({n\brace m}\) 表示将 \(n\) 件物品分成 \(m\) 个子集(非空)的方案数。
有
第一类斯特林数
| n | \(n\brack 0\) | \(n\brack 1\) | \(n\brack 2\) | \(n\brack 3\) | \(n\brack 4\) | \(n\brack 5\) | \(n\brack 6\) | \(n\brack 7\) | \(n\brack 8\) | \(n\brace9\) |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 1 | \(0\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 2 | \(0\) | \(1\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 3 | \(0\) | \(2\) | \(3\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 4 | \(0\) | \(6\) | \(11\) | \(6\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 5 | \(0\) | \(24\) | \(50\) | \(35\) | \(10\) | \(1\) | \(0\) | \(0\) | \(0\) | \(0\) |
| 6 | \(0\) | \(120\) | \(274\) | \(225\) | \(85\) | \(15\) | \(1\) | \(0\) | \(0\) | \(0\) |
| 7 | \(0\) | \(720\) | \(1764\) | \(1624\) | \(735\) | \(175\) | \(21\) | \(1\) | \(0\) | \(0\) |
| 8 | \(0\) | \(5040\) | \(13068\) | \(13132\) | \(6769\) | \(1960\) | \(322\) | \(28\) | \(1\) | \(0\) |
| 9 | \(0\) | \(40320\) | \(109584\) | \(11824\) | \(67284\) | \(22449\) | \(4536\) | \(546\) | \(36\) | \(1\) |
定义 \(n\brack m\) 表示将 \(n\) 件物品分成 \(m\) 个轮换(非空,圆排列)的方案数。
有
上升幂,下降幂,普通幂
斯特林数的性质
整理一下
表示普通幂时用第二类斯特林数,表示上升幂、下降幂时用第一类斯特林数,用大数表示小数时用 \((-1)^{n-i}\)。
考虑公式的嵌套,让斯特林数更毒瘤:
既然有二项式反演,也有斯特林反演!
证明略。
在来个柿子:
考虑组合意义来解释, \(x^n\) 表示将 \(n\) 个小球放进 \(x\) 个盒子的方案数, \(\sum_{i=0}^{x}\) 表示有几个盒子非空,\(\tbinom{x}{i}\) 表示选哪几个盒子非空, \(i!\) 表示非空盒子的顺序, \({n\brace i}\) 表示 \(n\) 个小球划分成 \(i\) 个集合的方案数。
二项式反演可得:
可以发现这是个卷积, \(NTT\) 即可计算同一行的第二类斯特林数。
例题
遇到不可以直接计算的多项式、组合数计数题时可以考虑普通幂转下降幂或上升幂。
其中
有
第二类斯特林数直接暴力 \(m^2\) 计算即可,总时间复杂度为 \(O(m^2)\)。
#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;
}
考虑组合意义来解释, \(x^n\) 表示将 \(n\) 个小球放进 \(x\) 个盒子的方案数, \(\sum_{i=0}^{x}\) 表示有几个盒子非空,\(\tbinom{x}{i}\) 表示选哪几个盒子非空, \(i!\) 表示非空盒子的顺序, \({n\brace i}\) 表示 \(n\) 个小球划分成 \(i\) 个集合的方案数。
二项式反演可得:
可以发现这是个卷积, \(NTT\) 即可计算同一行的第二类斯特林数。
#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;
}
有
考虑倍增:
假设我们求出了 \(x^{\overline{n}}\) 的多项式表达 \(f1\) 。
现在要求 \((x+n)^{\overline{n}}\) 的多项式表达 \(f2\) 。
有:
设 \(a_i\) 为 \(f1\) 的系数,有:
可以发现里面是个卷积, \(NTT\) 计算即可。
注意卷积时 \(Len\) 的长度不能大也不能小(代码第 \(48\) 行)。
#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;
}

浙公网安备 33010602011771号