luoguP4609 [FJOI2016] 建筑师

发布时间 2023-11-27 22:43:41作者: lu1no

题意:有n个高度1-n的楼房,从右看能看到a个,从左看能看到b个,问楼房有多少种排列方式。

分析:首先,高度为n的建筑是肯定不会被挡住的,可以把它作为一个分水岭,在它左边的被左边的建筑挡住,在它右边的被右边的建筑挡住。
由此我们可以把所有的建筑分成a+b-1个部分,每个部分由这个部分最高的建筑和被他所挡住的建筑组成,高n的建筑单独构成一个部分。
那么我们就可以把除了n以外的n-1个建筑放到a+b-2个圆桌上(n独坐一个桌),这时就是一个斯特林数。
为什么每一个部分是一个圆桌?因为最高的那个建筑的位置是固定的,所以如果那个块有x个建筑,就有(x - 1)!种排列方法,是一个圆排列。
所以最后的式子就是\({{n - 1}\brack{a + b - 2}} * \binom{a + b - 2}{a - 1}\)
对于第一类斯特林数,是将n个不同元素构成m个非空圆排列的方案数,有:

\({{n}\brack{m}} = {{n - 1}\brack{m - 1}} + {{n - 1}\brack{m}} *(n - 1)\)

\({{0}\brack{0}} = 1\)

\({{其他}\brack{0}} = 0\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int mod = 1e9 + 7, INF = 1 << 30;
const int N = 5e4 + 10, M = 210;
LL S[N][M], C[M][M];


void  init()
{
    S[0][0] = 1;
    for (int i = 1; i < N; i ++){
        for (int j = 1; j <= min(i, M - 1); j ++){
            S[i][j] = (S[i - 1][j - 1] + S[i - 1][j] * (i - 1)) % mod;
        }
    }
    for (int i = 0; i < M; i ++) C[i][0] = 1;
    for (int i = 1; i < M; i ++){
        for (int j = 1; j <= i; j ++){
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
}

void solve()
{
    LL n, a, b;
    cin >> n >> a >> b;
    LL ans = S[n - 1][a + b - 2];
    ans %= mod;
    ans = (ans * C[a + b - 2][a - 1]) % mod;
    cout << ans << endl;
}


int main()
{
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    init();
    int t = 1;
    cin >> t;
    while(t --) solve();
    return 0;
}