Count the Buildings
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2460 Accepted Submission(s): 804
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
2012 Multi-University Training Contest 8
专爆RE差评...
这道题大意就是说一个n高度不同的房屋, 从左边看能看到f个, 从右边看能看到b个. 问方案数.
实际上就是楼房就是排列... 那么从左往右和从右往左显然最后能看到的就是那个最高的房屋. 于是我们考虑那个最高的. 那么最高的左边有f - 1个可见房屋, 右边有b - 1个可见房屋. 那么我们把一个可见房屋到下一个可见房屋之前分为一段. 则最高的房屋左边就有f - 1段, 右边有b - 1段. 我们先考虑左边的f - 1段. 对于左边每一段, 因为由我们分段的方式, 这一段最高的房屋一定在这一段的左端——那么也就是说本段其他的元素可以随便排列. 那么实际上我们会发现每段就是一个圆排列. 右边的跟左边同样的分析方法. 那么我们现在相当于在除了最高的房屋, 剩下的n - 1个不同元素(房屋) 选出(f - 1) + (b - 1)个圆排列(段)来, 并选择f - 1个放在左边, 剩下的放在右边. 显然把f - 1个放在左边不用再讨论这f - 1段(圆排列)的先后次序, 因为题目中要求高度递增(从左到右), 那么选择了f - 1段后放在左边的方式就固定了. 右边同理. 那么对于n - 1个元素里选出f + b- 2个圆排列来——这显然就是第一类斯特林数的定义. 而后面的选择直接上组合数就可以了.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2005;
const int mod = 1e9 + 7;
int T, n, x, y;
long long c[maxn][maxn], s[maxn][maxn];
void init() {
for (int i = 0; i < maxn; ++ i) {
c[i][0] = 1;
s[i][0] = (!i) ? 1 : 0;
for (int j = 1; j <= i; ++ j) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
s[i][j] = (s[i - 1][j - 1] + (i - 1) * s[i - 1][j] % mod) % mod;
}
}
}
int main() {
init();
scanf("%d", &T);
while (T --) {
scanf("%d%d%d", &n, &x, &y);
if (x + y - 2 > 2000) puts("0");
else printf("%lld\n", s[n - 1][x + y - 2] * c[x + y - 2][x - 1] % mod);
}
}