斯特林数

斯特林数

第二类斯特林数

\({n \brace k}\) 表示 \(n\) 个元素划分为 \(k\) 个非空子集的方案数.

递推式:

\[{\Large \begin{aligned} & {n\brace k}={n-1\brace k-1}+k{n-1\brace k} \\ & 其中 {n\brace 0}=[n=0] \end{aligned} } \]

某些特殊值:

\[{\Large \begin{aligned} & {n\brace 0}=[n=0]\\ & {n\brace 1}={n\brace n}=1\\ & {n\brace 2}={n\brace n-1}=2^{n-1}-1 \end{aligned} } \]

通项公式(二项式反演证明):

\[{\Large \begin{aligned} & \left\{ \begin{array}{l} n \\ m \end{array} \right\} = \sum_{i=0}^{m} \frac{(-1)^{m-i} i^n}{i!(m-i)!} \end{aligned} } \]

第一类斯特林数

\({n\brack k}\) 表示 \(n\) 个元素划分为 \(k\) 个非空轮换的方案数。
如果将两个序列首尾相接后本质相同,那这两个序列就被视为一个轮换,如 \([a,b,c,d]\)\([b,c,d,a]\) 是一个轮换。
递推式:

\[{\Large {\Large \begin{aligned} & \left[ \begin{matrix} n \\ k \end{matrix} \right] = \left[ \begin{matrix} n-1 \\ k-1 \end{matrix} \right] + (n-1) \left[ \begin{matrix} n-1 \\ k \end{matrix} \right] \end{aligned} } } \]

重要关系:

\[{\Large \begin{aligned} & \sum_k^n{n\brack k}=n!\\ & {n\brack k}\ge {n\brace k}\\ & 当且仅当 n=k 或 k=n-1 时等号成立 \end{aligned} } \]

常规幂、下降幂与上升幂

常规幂:

\[{\Large \begin{aligned} & x^n \end{aligned} } \]

下降幂:

\[{ \Large \begin{aligned} & x^{\underline {k}}={x!\over (x-k)!} \end{aligned} } \]

上升幂:

\[{\Large \begin{aligned} & x^{\overline {k}}={(x+k-1)!\over (x-1)!} \end{aligned} } \]

常规幂、下降幂、上升幂间的互转以及转化成组合数形式

常规幂转下降幂:

\[{\Large \begin{aligned} & \ x^y = \sum_{k=0}^{y} {y\brace k} x^{\underline{k}}\\ & = \sum _{k=0}^y{y\brace k}\binom{x}{k} k! \end{aligned} } \]

下降幂转常规幂:

\[{\Large \begin{aligned} & \ x^{\underline y} = \sum_{k=0}^{y} (-1)^{(y-k)} {y\brack k} x^{{k}} \end{aligned} } \]

常规幂转上升幂:

\[{\Large \begin{aligned} & \ x^y = \sum_{k=0}^{y} {y\brace k} (-1)^{y-k} x^{\overline{k}} \end{aligned} } \]

上升幂转常规幂:

\[{\Large \begin{aligned} & x^{\overline{y}} = \sum_{k=0}^{y} \left[ \begin{array}{c} y \\ k \end{array} \right] x^k \end{aligned} } \]

下降幂转上升幂:

\[{\Large \begin{aligned} & x^{\underline{y}} = (-1)^y(-x)^{\overline y} \end{aligned} } \]

上升幂转下降幂:

\[{\Large \begin{aligned} & x^{\overline{y}} = \sum_{k=0}^{y} \binom{y}{k} \left[ \begin{array}{c} y \\ k \end{array} \right] x^{\underline{k}} \end{aligned} } \]

斯特林反演

\[{\Large \begin{aligned} & f(n)=\sum _{k=0}^n{n\brace k}g(k)\Leftrightarrow g(n)=\sum _{k=0}^n(-1)^{n-k}{n\brack k}f(k) \end{aligned} } \]

能够看出其形式与二项式反演类似,其用法也类似。

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

  • 式子本身相当复杂,看起来不太可做。然后看到有一项组合数就应该想到把普通幂转下降幂了。

  • 但是很可能转向一个误区,把 \(x^k\) 拆开来做。但是实际上除了 \(x^k\) 这一项本身以外没有一项包含 \(x\),因此转来转去也没有什么用。
    发现前面的多项式很复杂,最后怎么肯定都要拆开来做,不如就先把它拆开化简。

  • 由于除了 \(m\) 以外的数都很大,不太可能是复杂度的组成部分,因此要尽力把别的项消掉,把 \(m\) 提出来。
    发现对于下降幂与组合数相乘可以化简,把 \(k\) 弄进组合数里,因此先拆开化简。

  • \(b_i\) 为转为下降幂后 \(k^{\underline i}\) 的系数,有

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

显然对于 \(k<i\) 是无意义的,将枚举的 \(k\) 换成 \(k-i\)。再把多余的 \(x^i\) 提出去。

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

  • 这时发现 \(\sum_{k=0}^{n-i}\) 这一部分与 \(m\) 无关,满足二项式定理,把 \(k\) 化掉直接变成

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

  • 这时大部分东西我们都可以预处理出来,关键就变成了求 \(b_i\)。上面有普通幂转下降幂的公式,这里就简写:

\[{\Large b_{i} = \sum_{j=i}^{m} \left\{ \begin{array}{c} j \\ i \end{array} \right\} a_{j} } \]

  • 里外两个公式除了 \(\sum\) 以外的所有部分都可以预处理或者递推算到,因此总复杂度 \(O(m^2)\)

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+7;
int n,x,p,m,a[N],b[N],bce[N][N],ans;
int ksm(int bs,int k)
{
	int res=1;
	while(k){
		if(k&1) res=res*bs%p;
		bs=bs*bs%p,k>>=1;
	}return res;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>x>>p>>m;for(int i=0;i<=m;i++) cin>>a[i],a[i]%=p;
	bce[0][0]=bce[1][1]=1;int tmpn=1;
	for(int i=1;i<=m;i++)for(int j=1;j<=i;j++) bce[i][j]=(j*bce[i-1][j]%p+bce[i-1][j-1])%p;
	for(int i=0;i<=m;i++) for(int j=0;j<=i;j++) b[j]=(b[j]+bce[i][j]*a[i]%p)%p;
	for(int i=0;i<=m;i++) ans=(ans+b[i]*tmpn%p*ksm(x,i)%p*ksm(x+1,n-i)%p)%p,tmpn=tmpn*(n-i)%p;
	cout<<ans<<'\n';return 0;
}

P4609 [FJOI2016] 建筑师

这个更是结论题。

  • 首先需要观察到由于高度可以近似看做一个 \(n\) 的排列。那么最高的那栋楼会将整个序列分成不相干的两部分。
    由于两边本质相同因此我们先研究左半边,也就是会看到 \(A\) 个建筑物。

  • 问题的本质是确定 \(A-1\) 个建筑物被看到(最高的那个不算的话),其余的建筑物被挡住。
    所以我们要求的就是:\(A-1\) 个划定的较高建筑物依次排列,剩下的被挡住的任意排列。

  • 因此我们将那个较高的建筑以及后面被 其 挡住的建筑物划分为一组,我们只需要要求这个较高的建筑物固定在前面,后面的任意排列。

  • 发现这个定义与第一类斯特林数本质相同。这时就可以把左右两边合起来考虑,方案数为 \({n-1\brack A+B-2}\)。注意这里减二是因为左右两边重复了一组、最高的本身占了一组。
    然后我们要分 \(A-1\) 组在左边,乘法原理有总方案数为 \({n-1\brack A+B-2}{A+B-2\choose A-1}\)

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int s[50005][205],bin[205][205];
void solve()
{
	int n,a,b;cin>>n>>a>>b;
	cout<<s[n-1][a+b-2]*bin[a+b-2][a-1]%p<<'\n';
}
signed main()
{
	s[0][0]=1;
	for(int i=1;i<=50000;i++) for(int j=1;j<=min(i,200ll);j++)  
	s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%p;
	for(int i=0;i<=200;i++) bin[i][0]=1;for(int i=1;i<=200;i++) for(int j=1;j<=i;j++) bin[i][j]=(bin[i-1][j-1]+bin[i-1][j])%p;
	int T;cin>>T;while(T--) solve();
	return 0;
}
posted @ 2025-01-23 19:41  all_for_god  阅读(48)  评论(0)    收藏  举报