1. 程式人生 > >[FJOI2016]建築師(斯特林數)

[FJOI2016]建築師(斯特林數)

name std 嚴重 mil pri fin 部分 兩種 pac

【FJOI2016】建築師

問題描述

小 Z 是一個很有名的建築師,有一天他接到了一個很奇怪的任務:在數軸上建 n 個建築,每個建築的高度是 1 到 n 之間的一個整數。

小 Z 有很嚴重的強迫癥,他不喜歡有兩個建築的高度相同。另外小 Z 覺得如果從最左邊(所有建築都在右邊)看能看到 A個建築,從最右邊(所有建築都在左邊)看能看到 B 個建築,這樣的建築群有著獨特的美感。現在,小 Z 想知道滿足上述所有條件的建築方案有多少種?

如果建築 i 的左(右)邊沒有任何建造比它高,則建築 i 可以從左(右)邊看到。兩種方案不同,當且僅當存在某個建築在兩種方案下的高度不同。

輸入格式

第一行一個整數 T,代表 T 組數據。
接下來 T 行,每行三個整數 n,A,B

輸出格式

對於每組數據輸出一行答案 mod10^9+7。

樣例輸入 1

2
3 2 2
3 1 2

樣例輸出 1

2
1

樣例輸入 2

5
1 1 1
2 1 1
4 3 1
10 2 2
8 6 4

樣例輸出 2

1
0
3
219168
0

提示

對於 10% 的數據 : 1≤n≤10
對於 20% 的數據 : 1≤n≤100
對於 40% 的數據 : 1≤n≤50000, 1≤T≤5
對於 100%的數據 :1≤n≤50000, 1≤A,B≤100, 1≤T≤200000


顯然最高的那個一定看得見放在哪裏都一樣,我們不管它。

然後考慮最高的桿子左邊的部分(右邊同理)

每 兩個看得見的桿子之間都可能會有被擋住的桿子,我們把一個看得見的桿子和它擋住的桿子看成一個集合,假設這個集合大小為C,那麽在確定集合元素的情況下, 這個集合的排列的方案數實際上就是C個元素圓排列的方案數。因為一個圓排列都對應且僅對應一個合法排列(因為最高的桿子肯定在最左邊所以肯定是從最高的桿 子這裏斷開),所以問題就變成了把n-1個桿子分成a+b-2個圓排列的方案數(因為最高的桿子左邊有a-1個這樣的集合,右邊有b-1個),就 是$S(n-1,a+b-2)$。當然對於每種分法我們要確定這個圓排列是在左邊還是在右邊,所以要乘上$C(a+b-2,a-1)$。

答案就是$S(n-1,a+b-2)*C(a+b-2,a-1)$,預處理一下$O(1)$回答就好了。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=l; i<=r; i++)
 4 using namespace std;
 5 
 6 const int MOD=1000000007,M=210,N=50010;
 7 int s[N][M],C[M][M],T,n,a,b;
 8 
 9 void init(int n,int a) {
10     rep(i,0,n){
11         s[i][0]=0;
12         if(i<=a) s[i][i]=1;
13         rep(j,1,min(i,a)) s[i][j]=(0ll+s[i-1][j-1]+1ll*(i-1)*s[i-1][j]%MOD)%MOD;
14     }
15     rep(i,0,a){
16         C[i][0]=1;
17         rep(j,1,i) C[i][j]=(0ll+C[i-1][j]+C[i-1][j-1])%MOD;
18     }
19 }
20 
21 int main() {
22     init(50000,200);
23     for (scanf("%d",&T); T--; )
24     scanf("%d%d%d",&n,&a,&b),printf("%lld\n",(1ll*s[n-1][a+b-2]*C[a+b-2][a-1])%MOD);
25     return 0;
26 }                    

[FJOI2016]建築師(斯特林數)