LOJ #2173.「FJOI2016」【组合数学】【第一类斯特林数】

显然,对于任意方案,始终存在一个最高的建筑在中间,我们以此来划分左右进行讨论。

对于左边的 A A 个建筑,我们先讨论这 A A 个建筑的某一个建筑。

由于这个建筑能够被看到,那么就说明这个建筑可能挡住了若干个建筑,我们把这若干个建筑的数目记为 w w ,由于我们始终是看不到这 w w 个建筑的,所以这 w w 个建筑的排列对我们的答案无影响,而 w w 个建筑的排列数目为 w ! w!

接下来我们转换一下。

w w 的建筑的排列等价于给定 w + 1 w+1 个数,其中确定一个数放于队首,其他数随意排列的方案数,亦即 w + 1 w+1 个数的圆排列数目。

所以左边应该就有 A 1 A-1 个圆排列,同理右边应该有 B 1 B-1 个圆排列。

所以我们同时考虑,就变成了在 n 1 n-1 个数划分成 A 1 + B 1 A-1+B-1 个圆排列的方案数。

即第一类斯特林数: S n 1 A + B 2 S_{n-1}^{A+B-2}

但是由于我们还要将这 A + B 2 A+B-2 中的 A 1 A-1 个数放在最高建筑的左边,所以我们还要算上一个组合数: C A + B 2 A 1 C_{A+B-2}^{A-1}

于是我们得到最后的答案: A n s = S n 1 A + B 2 C A + B 2 A 1 Ans=S_{n-1}^{A+B-2}*C_{A+B-2}^{A-1}

#include <bits/stdc++.h>

#define ll long long

using namespace std;

const ll M=2e2+5;
const ll N=5e4+5;
const ll Mod=1e9+7;

ll t,n,a,b,C[M][M],S[N][M];

inline ll add(ll x,ll y) {
	return x+y>=Mod?x+y-Mod:x+y;
}

inline ll mul(ll x,ll y) {
	return x*y%Mod;
}

int main() {
	for(ll i=0;i<M;i++) C[i][0]=1;
	
	for(ll i=1;i<M;i++) for(ll j=1;j<=i;j++) 
		C[i][j]=add(C[i-1][j],C[i-1][j-1]);
	
	for(ll i=0;i<M;i++) S[i][i]=1;
	
	for(ll i=2;i<N;i++) for(ll j=1;j<min(i,M);j++) S[i][j]=add(mul(i-1,S[i-1][j]),S[i-1][j-1]);
	
	scanf("%lld",&t);
	
	while(t--) {
		scanf("%lld%lld%lld",&n,&a,&b);
		
		if(a+b>n+1){
			puts("0");continue;
		}
		
        printf("%lld\n",mul(C[a+b-2][a-1],S[n-1][a+b-2]));
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/83479678