斯特林(Stirling)数

斯特林(Stirling)数本文介绍了第一类斯特林数和第二类斯特林数的概念 包括它们在组合数学中的应用 如 nn 个元素的轮换划分和非空集合划分

大家好,欢迎来到IT知识分享网。

斯特林(Stirling)数

斯特林数是组合数学中,关于子集划分和置换划分的一类问题的解。其中,分为第一类斯特林数和第二类斯特林数。

第一类斯特林数

[ n k ] {n \brack k} [kn] 表示 n n n个元素排成 k k k个轮换。这里的轮换是指:

例如存在轮换 [ 1 , 2 , 3 ] [1,2,3] [1,2,3],那么意思就是存在三个相等序列 [ 1 , 2 , 3 ] = [ 2 , 3 , 1 ] = [ 3 , 1 , 2 ] [1,2,3]=[2,3,1]=[3,1,2] [1,2,3]=[2,3,1]=[3,1,2]

特别的,一个元素的轮换只有他本身,因为 [ 1 ] [1] [1]单个元素只能和自己轮换。两个元素的轮换也只有一个,因为 [ 1 , 2 ] = [ 2 , 1 ] [1,2]=[2,1] [1,2]=[2,1]是同一个轮换。

一般来说,对于 [ n 1 ] {n \brack 1} [1n],每一个 n n n个元素的全排列正好对应 n n n个轮换,因此根据除法原理:

[ n 1 ] = n ! n = ( n − 1 ) ! {n \brack 1} = \frac{n!}{n} = (n-1)! [1n]=nn!=(n1)!

事实上,还容易看出:

[ n n ] = 1 {n \brack n} = 1 [nn]=1

[ n n − 1 ] = ( n 2 ) {n \brack n-1} = \binom{n}{2} [n1n]=(2n)

对于最后一个等式,运用的鸽巢原理,相当于我们把 n n n个元素放进 n − 1 n-1 n1个箱子里面,并且不能放空,那么必有两个元素在同一个箱子里面,因为有两个元素的那个箱子的轮换相等,所以我们不用做任何处理。

现在我们考虑第一类斯特林数的递推公式,考虑 n n n个元素最后一个元素 n n n,对于他的安放方式有两种。第一种自己单独成为一个轮换,方案数为 [ n − 1 k − 1 ] {n-1 \brack k-1} [k1n1]。第二种,插入前 n − 1 n-1 n1个元素生成的 k k k个轮换中,方案数为 ( n − 1 ) [ n − 1 k ] (n-1){n-1 \brack k} (n1)[kn1](对于每一个轮换,假如这个轮换的大小为 ∣ S ∣ |S| S,那么插入一个元素变成一个新的轮换有 ∣ S ∣ |S| S种方式,即可以在每一个元素前面插入而不重复,因为 ∑ ∣ S i ∣ = ( n − 1 ) \sum |S_i|=(n-1) Si=(n1),故总共有 ( n − 1 ) (n-1) (n1)种插入方式),因此,我们的递推公式为:

[ n k ] = [ n − 1 k − 1 ] + ( n − 1 ) [ n − 1 k ] {n \brack k} = {n-1 \brack k-1} + (n-1){n-1 \brack k} [kn]=[k1n1]+(n1)[kn1]

其中,我们认为:

[ 0 0 ] = 1 [ n 0 ] = 0 ( n > 0 ) {0 \brack 0} = 1 \\ {n \brack 0} = 0 (n \gt 0) [00]=1[0n]=0(n>0)

例题

看不到的火柴数

LeetCode 5762

设答案为 P ( n , k ) P(n,k) P(n,k)

我们考虑最后一个位置能看到或者不能看到。如果能看到那么长度为 n n n的火柴必定在最后一个位置,方案数为 P ( n − 1 , k − 1 ) P(n – 1,k – 1) P(n1,k1)。如果看不到,那么最后一个位置上的元素可以是前 [ 1 , n − 1 ] [1,n-1] [1,n1]内的所有元素和元素 n n n做置换,相对大小不变,因此方案数为 ( n − 1 ) ∗ P ( n − 1 , k ) (n-1)*P(n-1,k) (n1)P(n1,k)。因此递推公式为:

P ( n , k ) = P ( n − 1 , k − 1 ) + ( n − 1 ) ∗ P ( n − 1 , k ) P(n,k) = P(n – 1,k – 1) + (n-1)*P(n-1,k) P(n,k)=P(n1,k1)+(n1)P(n1,k)

答案就是第一类斯特林数。 P ( n , k ) = [ n k ] P(n,k) = {n \brack k} P(n,k)=[kn]

高楼

HDU4372

我们枚举最高建筑 n n n的位置,他前面可以有 [ 0 , n − 1 ] [0,n-1] [0,n1]个建筑,故答案为:

a n s = ∑ i = 0 n − 1 [ i f − 1 ] [ n − i − 1 b − 1 ] ( n − 1 i ) ans = \sum_{i = 0}^{n-1} {i \brack f – 1} {n – i – 1 \brack b – 1} \binom{n – 1}{i} ans=i=0n1[f1i][b1ni1](in1)

查询次数很多,这么做必定会超时。因此我们必须使用一个神奇的公式化简这个和式。

具体的,我们把最高建筑 n n n放在最后面,构造 [ n − 1 f + b − 2 ] {n – 1 \brack f + b – 2} [f+b2n1],接着,然后把前面的 b − 1 b-1 b1个圆排列移到后面去,因此答案为:

a n s = [ n − 1 f + b − 2 ] ( f + b − 2 b − 1 ) ans = {n – 1 \brack f + b – 2} \binom{f + b – 2}{b – 1} ans=[f+b2n1](b1f+b2)

这样,我们得到了一个很重要的公式:

[ n l + m ] ( l + m l ) = ∑ k [ k l ] [ n − k m ] ( n k ) {n \brack l + m} \binom{l + m}{l} = \sum_k {k \brack l}{n-k \brack m} \binom{n}{k} [l+mn](ll+m)=k[lk][mnk](kn)

第二类斯特林数

第二类斯特林数 { n m } {n \brace m} {
mn}
是指,将 n n n个不同的元素划分为 m m m个相同的非空集合的方案数。

有如下递推式子:

{ n m } = { n − 1 m − 1 } + { n − 1 m } × m {n \brace m} = {n-1 \brace m-1} + {n-1 \brace m} \times m {
mn}
=
{
m1n1}
+
{
mn1}
×
m

我们考虑最后一个元素的划分,第一种方式将最后一个元素单独放在一个集合中,需要将前 n − 1 n-1 n1个元素划分成 m − 1 m-1 m1个集合。第二种情况将最后一个元素插入到之前已经划分好的集合中,有 m m m中插入方式,插入到每个集合都是不同的,方案数为 { n − 1 m } × m {n-1 \brace m} \times m {
mn1}
×
m

P1655 模板

#include <bits/stdc++.h> using namespace std; typedef long long ll; #define FR freopen("in.txt", "r", stdin) #define FW freopen("out.txt", "w", stdout) struct Decimal { 
    int bits[500]; int tot; Decimal() : Decimal(0) { 
    } Decimal(int init) : tot(0) { 
    fill(bits, bits + 500, 0); if (init == 0) { 
    tot = 1; return; } for (; init; init /= 10) { 
    bits[tot++] = init % 10; } } Decimal operator+(Decimal o) { 
    Decimal ans(0); int i = 0; for (; i < max(tot, o.tot); i++) { 
    ans.bits[i] += bits[i] + o.bits[i]; ans.bits[i + 1] += ans.bits[i] / 10; ans.bits[i] %= 10; } ans.tot = ans.bits[i] == 0 ? i : i + 1; return ans; } Decimal operator*(Decimal o) { 
    Decimal ans(0); for (int i = 0; i < tot; i++) { 
    for (int j = 0; j < o.tot; j++) { 
    ans.bits[i + j] += bits[i] * o.bits[j]; ans.bits[i + j + 1] += ans.bits[i + j] / 10; ans.bits[i + j] %= 10; } } int t = tot + o.tot - 1; while (ans.bits[t] == 0) t--; ans.tot = t + 1; return ans; } Decimal operator*(int k) { 
    Decimal fact(k); return (*this) * fact; } void output() { 
    for (int i = tot - 1; i >= 0; i--) { 
    putchar(bits[i] + '0'); } putchar('\n'); } }; Decimal stir[105][105]; int main() { 
    stir[0][0] = Decimal(1); for (int n = 1; n <= 101; n++) { 
    for (int m = 1; m <= min(101, n); m++) { 
    stir[n][m] = stir[n - 1][m - 1] + stir[n - 1][m] * m; } } int N, M; while (scanf("%d %d", &N, &M) != EOF) { 
    stir[N][M].output(); } return 0; } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/138366.html

(0)
上一篇 2025-06-13 16:20
下一篇 2025-06-13 16:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信