[FJOI2016]建築師(斯特林數)
【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]建築師(斯特林數)