Count the Buildings
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1477 Accepted Submission(s): 491
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.
2 3 2 2 3 2 1
2 1
ACdreamers的讲解:http://blog.youkuaiyun.com/acdreamers/article/details/9732431
题意:N座高楼,高度均不同且为1~N中的数,从前向后看能看到F个,从后向前看能看到B个,问有多少种可能的排列数。
0 < N, F, B <= 2000
首先我们知道一个结论:n的环排列的个数与n-1个元素的排列的个数相等,因为P(n,n)/n=(n-1)!。
可以肯定,无论从最左边还是从最右边看,最高的那个楼一定是可以看到的.
假设最高的楼的位置固定,最高楼的编号为n,那么我们为了满足条件,可以在楼n的左边分f-1组,右边分b-1组,且用每
组最高的那个元素代表这一组,那么楼n的左边,从左到右,组与组之间最高的元素一定是单调递增的,且每组中的最高元
素一定排在该组的最左边,每组中的其它元素可以任意排列(相当于这个组中所有元素的环排列)。右边反之亦然。
然后,可以这样考虑这个问题,最高的那个楼左边一定有f-1个组,右边一定有b-1个组,且每组是一个环排列,这就引出
了第一类Stirling数(个人分成
组,每组内再按特定顺序围圈的分组方法的数目)。
我们可以先把n-1个元素分成x-1+y-1组,然后每组内部做环排列。再在所有组中选取x-1组放到楼n的左边。所以答案是
ans(n, f, b) = C[f + b - 2][f - 1] * S[n - 1][f + b - 2];
我的理解:
如果不知道Stirling数,那会怎么去思考这题?很显然,最高的楼无论是从左边还是从右边都是能被看见的。
那么左边必须要有f-1座楼被看见,右边要有b-1座,就像下图。
左边的f-1座楼和右边的b-1座楼就是除最高楼之外的次高的f+b-2座楼了,这些楼哪些放左边哪些放右边的方案数就是从f+b-1座楼取f-1座楼的问题了。然后还有一点,就是高楼之间的矮的楼可以进行全排列。
所以这就很符合n的环排列的个数与n-1个元素的排列的个数相等,因为P(n,n)/n=(n-1)!这个性质了。也很符合Stirling数的解释:第一类Stirling数 s(p,k)
s(p,k)的一个的组合学解释是:将p个物体排成k个非空循环排列的方法数。
#include<stdio.h>
#include<string.h>
typedef long long ll;
const int N = 2005;
const ll MOD = 1000000007;
ll C[2005][2005];
ll stir[2005][2005];
void Init()
{
for(int i = 0; i < N; i++)
{
C[i][0] = 1;
C[i][i] = 1;
stir[i][0] = 0;
stir[i][i] = 1;
for(int j = 1; j < i; j++)
{
C[i][j] = (C[i-1][j]%MOD + C[i-1][j-1]%MOD)%MOD;
stir[i][j] = ((i-1)%MOD*stir[i-1][j]%MOD + stir[i-1][j-1]%MOD);
}
}
}
int main()
{
ll ans;
int t, n, f, b;
Init();
scanf("%d", &t);
while(t--)
{
scanf("%d%d%d", &n, &f, &b);
if(f+b > n+1)
{
puts("0");
continue;
}
ans = C[f+b-2][f-1]%MOD*stir[n-1][f+b-2]%MOD;
printf("%I64d\n", ans);
}
return 0;
}