斯特林数学习笔记

第一类斯特林数

定义

\(n\) 个互不相同的元素,划分为 \(k\) 个互不区分的非空轮换的方案数,记为 \(s(n,k)\),或 $n \brack m $。

一个轮换就是一个首尾相接的环形排列。如轮换 \([A,B,C,D]\),我们认为 \([A,B,C,D]=[B,C,D,A]=[C,D,A,B]=[D,A,B,C]\)。即,两个可以通过旋转而相互得到的轮换是等价的。需要注意,通过翻转得到的轮换不等价,如:\([A,B,C,D] \ne [D,C,B,A]\)

第一类斯特林数也可以形象理解为:\(n\) 个人坐 \(m\) 张圆桌(没有空桌)的方案数。

递推式

\({n \brack k} = {n-1 \brack k-1}+(n-1){n-1 \brack k}\)

其中边界为 \({n \brack 0}=[n==0]\)

递推式的证明可以考虑组合意义,新加入一个元素时,有两种方案:

  • 将该元素单独至于一个轮换中,那么共有 \(n-1 \brack k-1\) 种方案。

  • 将该元素加入当任意一个现有的轮换中,共有 \((n-1) {n-1 \brack k}\) 种方案。

第二类斯特林数

\(n\) 个互不相同的元素,划分为 \(m\) 个互不区分的非空子集的方案数。记为 \(n \brace m\),也可记为 \(S(n,m)\)

递推式

\({n \brace k}={n-1 \brace k-1}+k {n-1 \brace k}\)

其中边界为 \({n \brace 0}=[n==0]\)

递推式的证明可以考虑组合意义,新加入一个元素时,有两种方案:

  • 将该元素单独至于一个新集合中,那么共有 \(n-1 \brace k-1\) 种方案。

  • 将该元素加入当任意一个现有的轮换中,共有 \(k {n-1 \brace k}\) 种方案。

通项公式

利用二项式反演,可以得到第二类斯特林数的通项公式:

\({n \brace m}=\dfrac{\sum_{i=0}^{m} (-1)^{m-i} \binom{m}{i} i^n =\sum_{i=0}^{m}}{m!} =\sum_{i=0}^{m} \dfrac{(-1)^{m-i} i^n}{i!(m-i)!}\)

建筑师

对于一个 \(1,2,\ldots,n\) 的排列,设有 \(A\) 个数的左边都比它小,\(B\) 个数的右边都比它小,求满足的排列个数。

\(T\) 组询问,答案对 \(998244353\) 取模。

\(1 \leq n \leq 50000, \ 1 \leq A, B \leq 100, \ 1 \leq T \leq 200000\)

思路:

不难发现,\(n\) 一定满足题意,考虑以 \(n\) 为界限,在其左侧的属于 \(A\),在其右侧的属于 \(B\)

设第 \(i\) 个位置上的数为 \(p_i\)\(pre_i=\max_{1\leq j \leq i} p_i\)

那么对于一个在 \(A\) 中的数字,一定满足 \(pre_i=p_i\),在 \(B\) 中的同理。

\(i,j\) 为前后两个在 \(A\) 中的数,那么对于 \(k \in (i,j)\),一定满足 \(p_k < pre_k\)。考虑将 \([i,j-1]\) 划分为一个区间,那么最终可以得到 \(A+B-1\) 个区间,如下图所示:

image

其中黑色的方块表示该点在 \(A\)\(B\) 中,其中 \(n\) 单独成一个区间。

不难发现,划分为 \(A+B-1\) 个区间以后,区间内最大的数一定在区间的最左侧或最右侧。而对于其他的数,则可以全排列。

那么一个区间就等价于一个固定了开头的轮换,于是就可以套用第一类斯特林数,总的划分方案数为 \(n-1 \brack A+B-2\)

而一个区间既可以放在 \(A\) 中,也可以放在 \(B\) 中,将区间分组的方案数就为 \(\binom{A+B-2}{A-1}\)

于是对于每一组询问,答案就是 \(\binom{A+B-2}{A-1} {n-1 \brack A+B-2}\)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50010,M=210,mod=1e9+7;
int n,A,B,c[N][M],s[N][M];
int main()
{
	s[0][0]=1;for(int i=1;i<N;i++) for(int j=1;j<M;j++) s[i][j]=(s[i-1][j-1]+1ll*(i-1)*s[i-1][j]%mod)%mod;
	for(int i=0;i<M;i++) for(int j=0;j<=i;j++) if(!j) c[i][j]=1;else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	int T;scanf("%d",&T);while(T--) scanf("%d%d%d",&n,&A,&B),printf("%d\n",1ll*c[A+B-2][A-1]*s[n-1][A+B-2]%mod);
	return 0;
}

组合数问题

计算

\[\left(\sum_{k=0}^{n}f(k)\times x^k\times \binom{n}{k}\right)\bmod p \]

的值。其中 \(n\), \(x\), \(p\) 为给定的整数,\(f(k)\) 为给定的一个 \(m\) 次多项式 \(f(k) = a_0 + a_1k + a_2k^2 + \cdots + a_mk^m\)\(\binom{n}{k}\) 为组合数,其值为 \(\binom{n}{k} = \frac{n!}{k!(n-k)!}\)

\(1\le n, x, p \le 10^9, 0\le a_i\le 10^9, 0\le m \le \min(n,1000)\)

思路:

令 $x^{\underline{k} }=\prod_{i=x-k+1}^{x}=x(x-1)(x-2)\ldots (x-k+1) $。称为 \(x\)\(k\)下降幂

而对于单项式下降幂,与组合数存在以下联系:

\[\binom{n}{k} k^{\underline{m} }=\binom{n-m}{k-m} n^{\underline{m} } \]

考虑将多项式 \(f(k)=\sum_{i=0}^{m} a_i k^i\) 转换为 \(f(k)=\sum_{i=0}^{m} b_i k^{\underline{i} }\) 的形式。

对于一个单项式 \(x_n\),与第二类斯特林数存在以下等式:

\[x^n=\sum_{i=0}^{n} {n \brace i} \binom{x}{i} i!=\sum_{i=0}^{n} {n \brace i} x^{\underline{i}} \]

形象地理解一下,\(x^n\) 可以看成是将 \(n\) 个小球分别放到 \(x\) 个盒子当中,允许有空盒子的方案数。那么就可以考虑枚举哪些盒子不为空,然后就可以套用第二类斯特林数。由于盒子是不同的,所以最后还要乘上一个阶层。

于是就可以将 \(f(k)\) 变形:

\[f(k)=\sum_{i=0}^{m} a_i k^i=\sum_{i=0}^{m} a_i \sum_{j=0}^{i} {i \brace j} k^{\underline{j}} \]

\[=\sum_{i=0}^{m} k^{\underline{i}} \sum_{j=i}^{m} {j \brace i}a_j \]

得到 \(b_i\) 的表达式:

\[b_i=\sum_{j=i}^{m} {j \brace i}a_j \]

于是 \(b_i\) 就可以 \(O(m^2)\) 预处理出来。

接下来考虑对原式进行变形:

\[\sum_{k=0}^{n}f(k)\times x^k\times \binom{n}{k} \]

\[=\sum_{k=0}^{n} \sum_{i=0}^{m} b_i k^{\underline{i}} \times x^k\times \binom{n}{k} \]

利用上面的组合公式,可以得到:

\[\sum_{k=0}^{n} \sum_{i=0}^{m} b_i n^{\underline{i}} \times x^k\times \binom{n-i}{k-i} \]

\[=\sum_{i=0}^{m} b_i n^{\underline{i}} \sum_{k=0}^{n} \times x^k\times \binom{n-i}{k-i} \]

注意到当 \(k<i\) 时,后面的组合数没有意义,考虑内层枚举 \(k-i\) ,得到:

\[\sum_{i=0}^{m} b_i n^{\underline{i}} \sum_{k=0}^{n-i} \times x^{k+i}\times \binom{n-i}{k} \]

\[=\sum_{i=0}^{m} b_i n^{\underline{i}} x^i \sum_{k=0}^{n-i} \times x^{k}\times \binom{n-i}{k} \]

此时不难发现,内层循环就是一个二项式的展开形式,利用二项式定理,可以得到:

\[\sum_{i=0}^{m} b_i n^{\underline{i}} x^i (x+1)^{n-i} \]

由于题目中满足 \(m\leq 2000\),此时就可以直接枚举得到答案。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=2023;
int n,x,ans,p,m,a[N],b[N],s[N][N];
void Add(int &a,int b){a+=b;if(a>=p) a-=p;}
void Mul(int &a,int b){a=1ll*a*b%p;}
int mul(int a,int b){int res=1;while(b) ((b&1)&&(res=1ll*res*a%p,0)),a=1ll*a*a%p,b>>=1;return res;}
int main()
{
	scanf("%d%d%d%d",&n,&x,&p,&m);for(int i=0;i<=m;i++) scanf("%d",&a[i]);s[0][0]=1;
	for(int i=1;i<=m;i++) for(int j=1;j<=i;j++) s[i][j]=(s[i-1][j-1]+1ll*j*s[i-1][j]%p)%p;
	for(int i=0;i<=m;i++) for(int j=i;j<=m;j++) Add(b[i],1ll*a[j]*s[j][i]%p);int n_i=1,x_i=1;
	for(int i=0;i<=m;i++) Add(ans,1ll*b[i]*n_i%p*x_i%p*mul(x+1,n-i)%p),Mul(n_i,n-i),Mul(x_i,x);printf("%d\n",ans);
	return 0;
}
posted @ 2023-03-10 06:55  曙诚  阅读(68)  评论(0)    收藏  举报