题目描述
小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 n n 个建筑,每个建筑的高度是 到 n n 之间的一个整数。小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 个建筑,从最右边(所有建筑都在左边)看能看到 B B 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?
如果建筑 i 的左(右)边没有任何建造比它高,则建筑 i 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。
输入格式
第一行一个整数 ,代表 T T 组数据。
接下来 行,每行三个整数 n、A、B n 、 A 、 B 。
输出格式
对于每组数据输出一行答案 mod mod 109+7 10 9 + 7 。
样例输入
2
3 2 2
3 1 2
样例输出
2
1
数据规模与约定
对于 10% 10 % 的数据, 1≤n≤10 1 ≤ n ≤ 10 。
对于 20% 20 % 的数据, 1≤n≤100 1 ≤ n ≤ 100 。
对于 40% 40 % 的数据, 1≤n≤50000,1≤T≤5 1 ≤ n ≤ 50000 , 1 ≤ T ≤ 5 。
对于 70% 70 % 的数据, 1≤n≤50000,1≤T≤2000 1 ≤ n ≤ 50000 , 1 ≤ T ≤ 2000 。
对于 100% 100 % 的数据, 1≤n≤50000,1≤A、B≤100,1≤T≤200000 1 ≤ n ≤ 50000 , 1 ≤ A 、 B ≤ 100 , 1 ≤ T ≤ 200000 。
题解
首先,我们考虑暴力该怎么做。
记
fi,j,k
f
i
,
j
,
k
为 前
i
i
个中, 从左边能看到 个, 从右边能看到
k
k
个的方案数。
则
因为左右两边是镜像关系,所以我们只要处理出
fi,j
f
i
,
j
为放了
i
i
个,从一侧能看到 个的方案数即可。
这样就有
40
40
分了。
然后我们设去掉最高的点之后, 当前从左边能看到的为
a
a
个, 从右边能看到的为 个,我们每次加一条边有三种情况。
第一种情况为
a+1
a
+
1
, 第二种情况为
b+1
b
+
1
, 第三种情况为
a,b
a
,
b
不变。
对于第一第二种都各有
1
1
种情况。
对于第三种的可能随着我们选边的进行从 递减到
0
0
。
则设 为选了
i
i
个点,有 个点改变了
a
a
或 的方案数。
则
fi,j=fi−1,j−1+fi−1,j×(i−1)
f
i
,
j
=
f
i
−
1
,
j
−
1
+
f
i
−
1
,
j
×
(
i
−
1
)
然后其中有
A−1
A
−
1
个点是用来改变
a
a
的。
则最后的答案为
然后这是个斯特林数???
嗯没错,就是个第一类斯特林数。
所以答案为
CA−1A+B−2×SA+B−2n−1
C
A
+
B
−
2
A
−
1
×
S
n
−
1
A
+
B
−
2
额说一下为什么是第一类斯特林数吧。
我们设除去最高的点之后,每个点之后被挡住了
x
x
个点。
则这 个点有
x!
x
!
种方案。
算上挡住这些点的固定的那一个点,则这些点的方案数即为这些点的圆排列的方案数。
所以题意即为在去掉最高点之后的
n−1
n
−
1
个数中划分出
A+B−2
A
+
B
−
2
个圆排列并选出
A−1
A
−
1
个放在左边。
所以答案如上。
不知道有人是怎么打表看出来的,真的是神仙啊。
代码
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#define R register
#define ll long long
#define db double
#define sqr(_x) (_x) * (_x)
#define Cmax(_a, _b) ((_a) < (_b) ? (_a) = (_b), 1 : 0)
#define Cmin(_a, _b) ((_a) > (_b) ? (_a) = (_b), 1 : 0)
#define Max(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define Min(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define Abs(_x) (_x < 0 ? (-(_x)) : (_x))
using namespace std;
namespace Dntcry
{
inline int read()
{
R int a = 0, b = 1; R char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
return a * b;
}
inline ll lread()
{
R ll a = 0, b = 1; R char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
return a * b;
}
const int Maxn = 50010, Maxl = 210, Mod = 1000000007;
int T, n, A, B;
ll S[Maxn][Maxl], C[Maxl][Maxl];
void init(R int tmp1, R int tmp2)
{
for(R int i = 0; i <= tmp1; i++)
{
S[i][0] = 0;
if(i <= tmp2) S[i][i] = 1;
for(R int j = 1; j <= Min(i, tmp2); j++)
S[i][j] = (S[i - 1][j - 1] + (i - 1) * S[i - 1][j] % Mod) % Mod;
}
for(R int i = 0; i <= tmp2; i++)
{
C[i][0] = 1;
for(R int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
}
return ;
}
int Main()
{
init(50000, 200);
T = read();
while(T--)
{
n = read(), A = read(), B = read();
printf("%lld\n", S[n - 1][A + B - 2] * C[A + B - 2][A - 1] % Mod);
}
return 0;
}
}
int main()
{
return Dntcry :: Main();
}