LOJ #2173.「FJOI2016」【組合數學】【第一類斯特林數】
阿新 • • 發佈:2018-12-18
顯然,對於任意方案,始終存在一個最高的建築在中間,我們以此來劃分左右進行討論。
對於左邊的個建築,我們先討論這個建築的某一個建築。
由於這個建築能夠被看到,那麼就說明這個建築可能擋住了若干個建築,我們把這若干個建築的數目記為,由於我們始終是看不到這個建築的,所以這個建築的排列對我們的答案無影響,而個建築的排列數目為。
接下來我們轉換一下。
的建築的排列等價於給定個數,其中確定一個數放於隊首,其他數隨意排列的方案數,亦即個數的圓排列數目。
所以左邊應該就有個圓排列,同理右邊應該有個圓排列。
所以我們同時考慮,就變成了在個數劃分成個圓排列的方案數。
即第一類斯特林數:
但是由於我們還要將這中的個數放在最高建築的左邊,所以我們還要算上一個組合數:
於是我們得到最後的答案:
#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; }