初中生都看得懂的快速上手斯特林数指南——从盒放球问题说起

本文是一篇介绍斯特林数的初级教程,从盒放球问题出发,详细讲解了第二类斯特林数的定义、通项、特殊值及其计算方法,包括使用快速幂解决自然数幂和问题。此外,还涉及到了第一类斯特林数的概念,提供了相关例题的解析,适合初学者理解掌握斯特林数。
摘要由CSDN通过智能技术生成


第一次当标题党好方,但好像也没啥问题,因为我就是一个初中的菜鸡

前置知识

  1. 基础的组合计数知识
  2. NTT(非必要)
  3. 看到一长串的式子不会晕过去的能力

盒放球

盒放球问题可以描述为:

n n n个相同/不同的球, k k k个相同/不同的盒子,把 n n n个球放到盒子里,盒子允许/不允许为空,有多少种方案

稍微有点智商的人都知道,这一共有 2 × 2 × 2 = 8 2\times2\times2=8 2×2×2=8个问题
我们一个个来看

球相同,盒不同,不允许为空

这个非常简单,只需要使用隔板法就可以了,在 n − 1 n-1 n1个空中插入 k − 1 k-1 k1个板,把球隔成 k k k段,每一段对应地放到盒子中,所以有 ( n − 1 k − 1 ) \binom{n-1}{k-1} (k1n1)

球相同,盒不同,允许为空

这回变成了允许为空,怎么办?
很简单,先在每个盒子里都放一个球,这样就转化为了球相同,盒不同,不允许为空的情况了!一共有 ( n + k − 1 k − 1 ) \binom{n+k-1}{k-1} (k1n+k1)

球相同,盒相同,不允许/允许为空

这个需要使用到母函数(生成函数),不是本篇文章的重点,所以先不讲

球不同,盒不同,允许为空

这个应该没人不会吧……当然是 k n k^n kn种了

球不同,盒相同,不允许为空

emmm……
这是一个很有难度的问题,我们先看看如何递推吧
假设有 S ( n , k ) S(n,k) S(n,k)种,那么可能是前 n − 1 n-1 n1个球就用了 k k k个盒子,那么我们把第 n n n个球放在 k k k个盒子中的任意一个即可;如果前 n − 1 n-1 n1个球用了 k − 1 k-1 k1个盒子,那么我们就把第 n n n个球放在第 k k k个盒子中
所以 S ( n , k ) = S ( n − 1 , k ) × k + S ( n − 1 , k − 1 ) S(n,k)=S(n-1,k)\times k+S(n-1,k-1) S(n,k)=S(n1,k)×k+S(n1,k1)
话说我们不是要斯特林数吗?怎么还没讲?
别急,看看这个问题所使用的的字母: S S S,好像跟斯特林的首字母一样
不会吧不会吧,难道这就是传说中的斯特林数,原来斯特林数这么简单!!!对,您太强了,所以觉得简单,可以爆踩我这个菜鸡,妈妈再也不用担心你的学习啦!(大雾
接下来,我们暂且放下剩下的两种盒放球问题,来看看我们刚刚计算的 S ( n , k ) S(n,k) S(n,k),也就是第二类斯特林数的定义

第二类斯特林数

定义

n n n个元素分成 m m m无标号的集合 S ( n , m ) = { n m } S(n,m)=\begin{Bmatrix}n\\m\end{Bmatrix} S(n,m)={ nm}
等等,啥叫无标号集合啊?如果用上面的盒放球问题来解释,就是盒子是相同的,如果严谨一点说,就是如果一集合调换顺序后和另一集合完全相同(所有对应的集合都相等),那么这两集合就是等价的
比如 { { 1 } , { 2 , 3 } } \{\{1\},\{2,3\}\} { { 1},{ 2,3}} { { 2 , 3 } , { 1 } } \{\{2,3\},\{1\}\} { { 2,3},{ 1}}就是等价的
看到这里,相信有的人还是理解不了第二类斯特林数,所以,我们举一个例子: { 3 2 } = 3 \begin{Bmatrix}3\\2\end{Bmatrix}=3 { 32}=3
三种方法如下:

  1. { { 1 } , { 2 , 3 } } \{\{1\},\{2,3\}\} { { 1},{ 2,3}}
  2. { { 2 } , { 1 , 3 } } \{\{2\},\{1,3\}\} { { 2},{ 1,3}}
  3. { { 3 } , { 1 , 2 } } \{\{3\},\{1,2\}\} { { 3},{ 1,2}}

现在清楚多了吧?

通项

第二类斯特林数的通项为 { n m } = 1 m ! ∑ i = 0 m ( − 1 ) i ( m i ) ( m − i ) n \begin{Bmatrix}n\\m\end{Bmatrix}=\frac{1}{m!}\sum \limits_{i=0}^m(-1)^i\binom{m}{i}(m-i)^n { nm}=m!1i=0m(1)i(im)(mi)n
可以通过盒放球问题,来感性理解这个公式:我们考虑容斥,假设有 i i i个空盒,那么,我们先从 m m m个盒子中,选出 i i i个,剩下的盒子随便塞
最后,不要忘记无标号这个问题,所以我们需要除以 m ! m! m!
这个公式非常重要,它可以帮助我们用NTT计算出一整行的第二类斯特林数,这个后面会讲到

特殊值

在开始讲NTT求第二类斯特林数之前,为了让大家放松心情,我们先来看看第二类斯特林数的特殊值:
{ n 0 } = 0 , { n 1 } = 1 , { n n − 1 } = ( n 2 ) , { n n } = 1 \begin{Bmatrix}n\\0\end{Bmatrix}=0,\begin{Bmatrix}n\\1\end{Bmatrix}=1,\begin{Bmatrix}n\\n-1\end{Bmatrix}=\binom{n}{2},\begin{Bmatrix}n\\n\end{Bmatrix}=1 { n0}=0,{ n1}=1,{ nn1}=(2n),{ nn}=1
自己想吧,太简单了,不证了

计算第二类斯特林数

Warning!Warning!Warning!前方高能,请没学过NTT迅速跳过这一节
好的,我们开始推式子吧!
{ n m } = 1 m ! ∑ i = 0 m ( − 1 ) i ( m i ) ( m − i ) n = 1 m ! ∑ i = 0 m ( − 1 ) i m ! i ! ( m − i ) ! ( m − i ) n = ∑ i = 0 m ( − 1 ) i i ! ( m − i ) n ( m − i ) ! \begin{Bmatrix}n\\m\end{Bmatrix}=\frac{1}{m!}\sum \limits_{i=0}^m(-1)^i\binom{m}{i}(m-i)^n=\frac{1}{m!}\sum \limits_{i=0}^m(-1)^i\frac{m!}{i!(m-i)!}(m-i)^n=\sum \limits_{i=0}^m\frac{(-1)^i}{i!}\frac{(m-i)^n}{(m-i)!} { nm}=m!1i=0m(1)i(im)(mi)n=m!1i=0m(1)ii!(mi)!m!(mi)n=i=0mi!(1)i(mi)!(mi)n
看,这是什么?是不是一个漂亮的卷积式?
还没看出来?设 f ( x ) = ∑ i ⩾ 0 ( − 1 ) i i ! x i , g ( x ) = ∑ i ⩾ 0 i n i ! x i f(x)=\sum \limits_{i\geqslant 0} \frac{(-1)^i}{i!}x^i,g(x)=\sum\limits_{i\geqslant 0}\frac{i^n}{i!}x^i f(x)=i0i!(1)ixi,g(x)=i0i!inxi,则 { n m } \begin{Bmatrix}n\\m\end{Bmatrix} { nm}就是 f ( x ) f(x) f(x) g ( x ) g(x) g(x)的卷积
所以,我们就可以愉快地使用逆天塔(NTT)了!
模板题代码:

#include<bits/stdc++.h>
#define ljc 167772161
#define int long long
#define ll long long
using namespace std;
int n,m,k,r[800006],lim,w[800006],a[800006],b[800006],inv[800006];
inline ll fast_pow(ll a,ll b,ll p){
   
    ll t=1;a%=p;
    while (b){
   
        if (b&1) t=t*a%p;
        b>>=1;a=a*a%p;
    }
    return t;
}
inline void NTT(ll f[],int lim,int id){
   
    for (int i=0;i<lim;i++){
   
        if (i<r[i]) swap(f[r[i]],f[i]);
    }
    w[0]=1;
    for (int len=1;len<lim;len<<=1){
   
        ll gen=fast_pow(3,(ljc-1)/(len<<1)*id+ljc-1,ljc);
        for (int i=1;i<len;i++) w[i]=w[i-1]*gen%ljc;
        for (int i=0;i<lim;i+=len<<1){
   
            ll *f1=f+i,*f2=f1+len;
            for (int j=0;j<len;j++){
   
                ll x=f1[j]%ljc,y=f2[j]*w[j]%ljc;
                f1[j]=(x+y)%ljc;f2[j]=(x-y+ljc)%ljc;
            }
        }
    }
    if (id==1) return;
    ll Inv=fast_pow(lim,ljc-2,ljc);
    for (int i=0;i<lim;i++) f[i]=f[i]*Inv%ljc;
}
signed main(){
   
    cin>>n;n++;
    inv[0]=inv[1]=1;
    for (int i=2;i<n;i++) inv[i]=(ljc-(ljc/i)*inv[ljc%i]%ljc)%ljc;
    for (int i=1;i<n;i++) inv[i]=inv[i-1]*inv[i]%ljc;
    for (int i=0,one=1;i<n;one=ljc-one,i++){
   
        a[i]=one*inv[i]%ljc;b[i]=fast_pow(i,n-1,ljc)*inv[i]%ljc;
    }
    int lim=1,len=0;
    while (lim<=(n<<1)) lim<<=1,len++;
    for (int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    NTT(a,lim,1);NTT(b,lim,1);
    for (int i=0;i<lim;i++) a[i]=a[i]*b[i]%ljc;
    NTT(a,lim,-1);
    for (int i=0;i<n;i++) printf("%lld ",a[i]);
}

自然数幂和

你有没有见过这样的题目:让你求 ∑ i = 1 n i k \sum\limits_{i=1}^ni^k i=1nik的题目?
你可能会想:这不简单吗,每次使用快速幂不就好了嘛
不!没有那么简单! n ⩽ 1 0 9 , k ⩽ 5000 n\leqslant 10^9,k\leqslant 5000 n109,k5000的时候你还能用快速幂吗?不能!不要告诉我你要分段打表
但是!第二类斯特林数可以!
这只能说明:斯特林数(停顿)行!快速幂(停顿)不行!我爱斯特林数!老伏拉夫了
那斯特林数到底怎么行了呢?我们要从一个假快速幂讲起

快速幂?

n m = ∑ i = 0 m { m i } n i ‾ n^m=\sum \limits_{i=0}^{m} \begin{Bmatrix}m\\i\end{Bmatrix} n^{\underline{i}} nm=i=0m{ mi}ni
其中 n i ‾ n^{\underline{i}} ni表示 n n n i i i次下降幂,即 n i ‾ = ∏ k = 0 i − 1 ( n − k ) n^{\underline{i}}=\prod \limits_{k=0}^{i-1}(n-k) ni=k=0i1(nk)
证明:
考虑归纳证明,当 m = 1 m=1 m=1时, ∑ i = 0 m { m i } n i ‾ = { 1 1 } n = n \sum \limits_{i=0}^{m} \begin{Bmatrix}m\\i\end{Bmatrix} n^{\underline{i}}=\begin{Bmatrix}1\\1\end{Bmatrix} n=n i=0m{ mi}ni={ 11}n=n
假设 m = k m=k m=k时成立
m = k + 1 m=k+1 m=<

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值