1. 程式人生 > >LOJ #2173.「FJOI2016」【組合數學】【第一類斯特林數】

LOJ #2173.「FJOI2016」【組合數學】【第一類斯特林數】

顯然,對於任意方案,始終存在一個最高的建築在中間,我們以此來劃分左右進行討論。

對於左邊的AA個建築,我們先討論這AA個建築的某一個建築。

由於這個建築能夠被看到,那麼就說明這個建築可能擋住了若干個建築,我們把這若干個建築的數目記為ww,由於我們始終是看不到這ww個建築的,所以這ww個建築的排列對我們的答案無影響,而ww個建築的排列數目為w!w!

接下來我們轉換一下。

ww的建築的排列等價於給定w+1w+1個數,其中確定一個數放於隊首,其他數隨意排列的方案數,亦即w+1w+1個數的圓排列數目。

所以左邊應該就有A1A-1個圓排列,同理右邊應該有B1B-1

個圓排列。

所以我們同時考慮,就變成了在n1n-1個數劃分成A1+B1A-1+B-1個圓排列的方案數。

即第一類斯特林數:Sn1A+B2S_{n-1}^{A+B-2}

但是由於我們還要將這A+B2A+B-2中的A1A-1個數放在最高建築的左邊,所以我們還要算上一個組合數:CA+B2A1C_{A+B-2}^{A-1}

於是我們得到最後的答案:Ans=Sn1A+B2CA+B2A1Ans=S_{n-1}^{A+B-2}*C_{A+B-2}^{A-1}

A1

#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;
}