[HDU 4372]Count the Buildings(第一类斯特林数+组合数)

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=4372

题目大意

n 个建筑排成一列,它们的高度是在 1n 之间,且没有两个建筑高度相同。从前面看能看到 F 个建筑,从后面看能看到 B 个建筑,问这样的建筑排列共有多少种。

思路

第一类斯特林数 s1[i][j]=i 个互不相同的物品,划分成 j 个环的方案数。

s1[i][j]=s1[i1][j1]+(i1)s1[i1][j]

递推式的解释:对于第 i 个物品,如果前 i1 个物品构成 j1 个环,那么它可以单独构成第 j 个环。如果前 i1 个物品已经构成了 j 个环,那么它可以选择放在其中一个物品后面,共 i1 个选法,故为 (i1)s1[i1][j]

实际上,一个长度为 n 的环的排列的个数就等于 n1 个数排列的个数 ,这是很显然的,因为要为了避免重复计算方案数,断环为链,并约定序列最前面永远是元素1,这样就是要求剩下的 n1 个元素的排列,即环排列数等于 (n1)! ,证毕。

[HDU 4372]Count the Buildings(第一类斯特林数+组合数)_第1张图片
注意到建筑物所有的排列的情形都类似于上图:所有的橙色建筑都是能从前方或者后方被看到的,所有的黄色建筑都是看不到的。并且最高的那个建筑物从前或从后都能看到,能看到的建筑物总数为 F+B1 。我们把所有的建筑物划分成 F+B1 组,每组里只能看到组内最高的那个元素。那么在最高的楼前面的组里,每组里最高的楼一定是在每组里的最前面,在最高的楼后面的组里,每组里最高的楼一定是在每组里的最后面,那么假如确定了一个组里的元素,并且组里共 n 个元素,则这个组的所有排列的方案数是 (n1)! ,根据上面所说的性质 ,就是 n 个元素的环排列个数了。

以最高的那个建筑物为中点分开看,能被前面看到的建筑物(不算最高的那个)的个数是 F1 ,能被后面看到的建筑物(不算最高的那个)的个数是 B1 ,这意味着最高的楼的左边有 F1 组,右边有 B1 组。

除掉最高的那个建筑后,还有 n1 个建筑,它们分成 F+B2 组环排列,可以构成 s1[n1][F+B2] 个方案,然后我们要在这 F+B2 组里,选择其中 F1 组放到前面, B1 组放到后面,这个操作的方案数是 CF1F+B2 。根据乘法原理,最终的答案为:

ansn,F,B=CF1F+B2s1[n1][F+B2]

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 2200
#define MOD 1000000007

using namespace std;

typedef long long int LL;

LL s1[MAXN][MAXN],C[MAXN][MAXN];

void GetCombination()
{
    for(int i=1;i<MAXN;i++)
    {
        C[i][0]=1%MOD;
        C[i][i]=1%MOD;
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    }
}

void GetFirstStirling()
{
    for(int i=1;i<MAXN;i++)
    {
        s1[i][0]=0;
        s1[i][i]=1;
        for(int j=1;j<i;j++)
            s1[i][j]=(((i-1)*s1[i-1][j])%MOD+s1[i-1][j-1])%MOD;
    }
}

int main()
{
    GetCombination();
    GetFirstStirling();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        LL n,f,b;
        scanf("%lld%lld%lld",&n,&f,&b);
        printf("%lld\n",C[f+b-2][f-1]*s1[n-1][f+b-2]%MOD);
    }
    return 0;
}

你可能感兴趣的:([HDU 4372]Count the Buildings(第一类斯特林数+组合数))