斯特林数
斯特林数
第二类斯特林数
\({n \brace k}\) 表示 \(n\) 个元素划分为 \(k\) 个非空子集的方案数.
递推式:
某些特殊值:
通项公式(二项式反演证明):
第一类斯特林数
\({n\brack k}\) 表示 \(n\) 个元素划分为 \(k\) 个非空轮换的方案数。
如果将两个序列首尾相接后本质相同,那这两个序列就被视为一个轮换,如 \([a,b,c,d]\) 与 \([b,c,d,a]\) 是一个轮换。
递推式:
重要关系:
常规幂、下降幂与上升幂
常规幂:
下降幂:
上升幂:
常规幂、下降幂、上升幂间的互转以及转化成组合数形式
常规幂转下降幂:
下降幂转常规幂:
常规幂转上升幂:
上升幂转常规幂:
下降幂转上升幂:
上升幂转下降幂:
斯特林反演
能够看出其形式与二项式反演类似,其用法也类似。
P6620 [省选联考 2020 A 卷] 组合数问题
-
式子本身相当复杂,看起来不太可做。然后看到有一项组合数就应该想到把普通幂转下降幂了。
-
但是很可能转向一个误区,把 \(x^k\) 拆开来做。但是实际上除了 \(x^k\) 这一项本身以外没有一项包含 \(x\),因此转来转去也没有什么用。
发现前面的多项式很复杂,最后怎么肯定都要拆开来做,不如就先把它拆开化简。 -
由于除了 \(m\) 以外的数都很大,不太可能是复杂度的组成部分,因此要尽力把别的项消掉,把 \(m\) 提出来。
发现对于下降幂与组合数相乘可以化简,把 \(k\) 弄进组合数里,因此先拆开化简。 -
设 \(b_i\) 为转为下降幂后 \(k^{\underline i}\) 的系数,有
显然对于 \(k<i\) 是无意义的,将枚举的 \(k\) 换成 \(k-i\)。再把多余的 \(x^i\) 提出去。
- 这时发现 \(\sum_{k=0}^{n-i}\) 这一部分与 \(m\) 无关,满足二项式定理,把 \(k\) 化掉直接变成
- 这时大部分东西我们都可以预处理出来,关键就变成了求 \(b_i\)。上面有普通幂转下降幂的公式,这里就简写:
- 里外两个公式除了 \(\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;
}

浙公网安备 33010602011771号