HDU4372 Count the Buildings —— 组合数 + 第一类斯特林数
题目链接:https://vjudge.net/problem/HDU-4372
Count the Buildings
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2509 Accepted Submission(s): 815
Now, given N, F, B, your task is to figure out how many ways all the buildings can be.
Next T lines, each line consists of three integer N, F, B, (0<N, F, B<=2000) described above.
题意:
有n幢高度不一的楼房位于一条直线上,问有多少种方案数,使得人从第一幢往前看时看到f幢,从最后一幢往后看时看到b幢?
题解:
1.可知最高的那栋必定能够看到,于是就分成了左边和右边。
2.对于左边的楼而言,需要把他们分成f-1组,每一组的最高楼在最左边,这样就把组内其他的楼遮住了,于是就看到f-1栋。对于右边的也如此。
3.那怎么分组呢?首先,求出求出第一类斯特林数 S[n-1][f-1+b-1],即把除了最高楼之外的楼房排成f-1+b-1个圈。由于每一组中最高的楼房固定在左边或右边,这样就对应了圈。换句话说,对排列好的一个圈选定一栋楼房,而这栋楼房就是最高的那栋,然后再把这个圈展开成一列,这样就对应了一组。所以可以用第一类斯特林数求出分组的方案数。
4.分好组后,就直接从 f+b-2组中抽取f-1组放在左边(由于要求递增,所以选出来之后他们的位置就固定了,不需要再排列),总有 C[f+b-2][f-1]种选择。
5.综上,总共有 S[n-1][f-1+b-1] * C[f+b-2][f-1] 种方案数。注意, 当n-1<f+b-2时, 问题无解。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 2e3+10; 18 19 LL S[MAXN][MAXN], C[MAXN][MAXN]; 20 21 void init() 22 { 23 for(int i = 0; i<MAXN; i++) 24 { 25 C[i][0] = 1; 26 for(int j = 1; j<=i; j++) 27 C[i][j] = (C[i-1][j-1]+C[i-1][j])%MOD; 28 } 29 30 memset(S, 0, sizeof(S)); 31 for(int i = 1; i<MAXN; i++) 32 { 33 S[i][0] = 0; S[i][i] = 1; 34 for(int j = 1; j<i; j++) 35 S[i][j] = (((i-1)*S[i-1][j])%MOD + S[i-1][j-1])%MOD; 36 } 37 } 38 39 int main() 40 { 41 init(); 42 int T, n, f, b; 43 scanf("%d", &T); 44 while(T--) 45 { 46 scanf("%d%d%d", &n, &f, &b); 47 LL ans; 48 if(n-1<f+b-2) ans = 0; 49 else ans = (1LL*S[n-1][f+b-2]*C[f+b-2][f-1])%MOD; 50 printf("%lld\n", ans); 51 } 52 }