HDU4372Count the Buildings(第一类斯特林数)

本文介绍了一道关于高楼排列的问题,给出了详细的解析过程与代码实现。通过确定可见高楼数量,利用组合数学中的第一类Stirling数求解高楼的不同排列方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


Problem Description
There are N buildings standing in a straight line in the City, numbered from 1 to N. The heights of all the buildings are distinct and between 1 and N. You can see F buildings when you standing in front of the first building and looking forward, and B buildings when you are behind the last building and looking backward. A building can be seen if the building is higher than any building between you and it.
Now, given N, F, B, your task is to figure out how many ways all the buildings can be.
 

Input
First line of the input is a single integer T (T<=100000), indicating there are T test cases followed.
Next T lines, each line consists of three integer N, F, B, (0<N, F, B<=2000) described above.
 

Output
For each case, you should output the number of ways mod 1000000007(1e9+7).
 

Sample Input
  
2 3 2 2 3 2 1
 

Sample Output
  
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个人分成k组,每组内再按特定顺序围圈的分组方法的数目)。


我们可以先把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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值