这题一直往 $\text{dp}$ 去想,然后就没了

所以其实并不用注意之前的可看见的建筑的最大值是多少,若把那些能够被看见的建筑之间构成的区间看作一个小组,那么能够被看见的建筑不过是在当前小组中选择一个最大值最为代表排在左(右)边罢了

所以现在问题转化为将 $n - 1$ 个建筑分在 $A + B - 2$ 个盒子,然后盒子内可以随意排列的个数

然后上述问题实际上可以将每个盒子看作一个圆,故问题又转化为 $n - 1$ 个建筑构成的 $A + B - 2$ 个圆排列方案数,即斯特林数 $S (n - 1, A + B - 2)$

然后要在其中选择一些放左(右)边,故答案即为
$$
ans = S (n - 1, A + B - 2) \times \dbinom{A + B - 2}{A - 1}
$$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <cstdio>
#include <cstring>

#define MOD 1000000007

using namespace std;

typedef long long LL;

const int MAXN = 5e04 + 10;
const int MAXAB = 200 + 10;

int T;
int n, A, B;

const int NMAX = 5e04, ABMAX = 200;
LL S[MAXN][MAXAB]= {0};
LL fact[MAXAB]= {0}, invfact[MAXAB]= {0};
void init () {
S[0][0] = 1;
for (int i = 1; i <= NMAX; i ++)
for (int j = 1; j <= min (i, ABMAX); j ++)
S[i][j] = (S[i - 1][j - 1] + S[i - 1][j] * (i - 1) % MOD) % MOD;
fact[0] = fact[1] = invfact[0] = invfact[1] = 1;
for (int i = 2; i <= ABMAX; i ++) {
fact[i] = fact[i - 1] * i % MOD;
invfact[i] = (MOD - MOD / i) * invfact[MOD % i] % MOD;
}
for (int i = 1; i <= ABMAX; i ++)
invfact[i] = invfact[i - 1] * invfact[i] % MOD;
}
inline LL C (int n, int m) {
return fact[n] * invfact[m] % MOD * invfact[n - m] % MOD;
}

inline int getnum () {
int num = 0; char ch = getchar ();
while (! isdigit (ch)) ch = getchar ();
while (isdigit (ch)) num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
return num;
}
inline void write (LL x) {
if (x >= 10) write (x / 10);
putchar (x % 10 + '0');
}

int main () {
init ();
T = getnum ();
for (int Case = 1; Case <= T; Case ++) {
n = getnum (), A = getnum (), B = getnum ();
LL ans = S[n - 1][A + B - 2] * C (A + B - 2, A - 1) % MOD;
write (ans), puts ("");
}

return 0;
}

/*
2
3 2 2
3 1 2
*/