2023-04-16 17:27阅读: 220评论: 0推荐: 0

斯特林数,上升幂,下降幂学习笔记

斯特林数,上升幂,下降幂,普通幂的定义

第二类斯特林数

n {n0} {n1} {n2} {n3} {n4} {n5} {n6} {n7} {n8} {n9}
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

定义 {nm} 表示将 n 件物品分成 m 个子集(非空)的方案数。

{nm}={n1m1}+m{n1m}

第一类斯特林数

n [n0] [n1] [n2] [n3] [n4] [n5] [n6] [n7] [n8] {n9}
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

定义 [nm] 表示将 n 件物品分成 m 个轮换(非空,圆排列)的方案数。

[nm]=(n1)[n1m]+[n1m1]

上升幂,下降幂,普通幂

xn=x!(xn)!xn=(x+n1)!(x1)!xn=xnxn=(x+n1)nxn=(xn+1)n

斯特林数的性质

i=0n{ni}xi=i=0n(i{n1i}+{n1i1})xi=i=0ni{n1i}xi+i=0n{n1i1}xi=i=0ni{n1i}xi+i=1n{n1i1}xi=i=0ni{n1i}xi+i=1n{n1i1}x(i1)+1=i=0ni{n1i}xi+i=0n1{n1i}xi+1=i=0n1i{n1i}xi+i=0n1{n1i}xi(xi)=i=0n1i{n1i}xi+{n1i}xi(xi)=xi=0n1{n1i}xi=x×xn1=xn

i=0n[ni]xi=i=0n((n1)[n1i]+[n1i1])xi=i=0n(n1)[n1i]xi+i=0n[n1i1]xi=i=0n(n1)[n1i]xi+i=1n[n1i1]x(i1)+1=i=0n(n1)[n1i]xi+i=0n1[n1i]xi+1=(n1+x)i=0n[n1i]xi=(x+n1)xn1=xn

xn=(1)n(x)n=(1)ni=0n[ni](x)i=i=0n[ni](1)nixi

xn=(1)n(x)n=(1)ni=0n{ni}(x)i=(1)ni=0n{ni}(1)ixi=i=0n{ni}(1)nixi

整理一下

xn=i=0n{ni}xixn=i=0n{ni}(1)nixixn=i=0n[ni]xixn=i=0n[ni](1)nixi

表示普通幂时用第二类斯特林数,表示上升幂、下降幂时用第一类斯特林数,用大数表示小数时用 (1)ni

考虑公式的嵌套,让斯特林数更毒瘤

xn=i=0n{ni}(1)nixi=i=0n{ni}(1)nim=0i[im]xm=m=0nxmi=mn{ni}[im](1)ni

i=mn{ni}[im](1)ni=[n=m]

xn=i=0n[ni](1)nixi=i=0n[ni](1)nij=0i{ij}xj=j=0ixji=jn[ni]{ij}(1)ni

i=mn[ni]{im}(1)ni=[m=n]

既然有二项式反演,也有斯特林反演!

g(n)=i=0n{ni}f(i)f(n)=i=0n[ni](1)nig(i)g(n)=i=0n[ni]f(i)f(n)=i=0n{ni}(1)nig(i)

证明略

在来个柿子:

xn=i=0x{ni}(xi)i!

考虑组合意义来解释, xn 表示将 n 个小球放进 x 个盒子的方案数, i=0x 表示有几个盒子非空,(xi) 表示选哪几个盒子非空, i! 表示非空盒子的顺序, {ni} 表示 n 个小球划分成 i 个集合的方案数。

二项式反演可得:

xn=i=0x{ni}(xi)i!{nm}m!=i=0m(mi)(1)miin

{nm}=i=0m(mi)(1)miinm!=i=0mm!i!(mi)!m!(1)miin=i=0min(1)mii!(mi)!=i=0mini!×(1)mi(mi)!

可以发现这是个卷积, NTT 即可计算同一行的第二类斯特林数。

例题

P6620 [省选联考 2020 A 卷] 组合数问题

遇到不可以直接计算的多项式、组合数计数题时可以考虑普通幂转下降幂或上升幂。

(k=0nf(k)xk(nk))modp=i=0mk=0nbikixk(nk)=i=0mk=0nbinixk(niki)=i=0mbinik=0n(niki)xk=i=0mbinik=0ni(niki)xk1nik=i=0mbinixik=0ni(nik)xk1nik=i=0mbinixi(x+1)ni

其中

f(x)=i=0mbixi

f(x)=i=0maixi=i=0maij=0i{ij}xj=j=0mxji=jm{ij}ai

第二类斯特林数直接暴力 m2 计算即可,总时间复杂度为 O(m2)

#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;
}

P5395 第二类斯特林数·行

xn=i=0x{ni}(xi)i!

考虑组合意义来解释, xn 表示将 n 个小球放进 x 个盒子的方案数, i=0x 表示有几个盒子非空,(xi) 表示选哪几个盒子非空, i! 表示非空盒子的顺序, {ni} 表示 n 个小球划分成 i 个集合的方案数。

二项式反演可得:

xn=i=0x{ni}(xi)i!{nm}m!=i=0m(mi)(1)miin

{nm}=i=0m(mi)(1)miinm!=i=0mm!i!(mi)!m!(1)miin=i=0min(1)mii!(mi)!=i=0mini!×(1)mi(mi)!

可以发现这是个卷积, 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;
}

P5408 第一类斯特林数·行

xn=i=0n[ni]xi

考虑倍增:

x2n=xn(x+n)n

假设我们求出了 xn 的多项式表达 f1

现在要求 (x+n)n 的多项式表达 f2

有:

f1(x)=f2(xn)

aif1 的系数,有:

f2=i=0nai(x+n)i=i=0naij=0ixjnij(ij)=j=0nxji=jnnijaii!j!(ij)!=j=0nxjj!i=jnaii!nij(ij)!

可以发现里面是个卷积, 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;
}

本文作者:Fzrcy's Blog

本文链接:https://www.cnblogs.com/fzrcy/p/17323656.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   fzrcy  阅读(220)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.