题目大意
思路
个人觉得这位巨佬的博客讲的异常之好。我们把所有建筑物中最高的那个玩意提出来(塔的高度是1~n),我们知道无论从哪边看,这个最高的塔一定能够被看到,所以,我们就以这座塔为分界线。我们考虑,左边需要有
F
−
1
F-1
F−1幢,我们想一下能看到它们的条件,就是这
F
−
1
F-1
F−1幢是单调上升的,以此类推,右边
B
−
1
B-1
B−1幢能看到的条件就是从左往右单调递减。我们对于左边能够看到的
F
−
1
F-1
F−1幢,我们设
a
i
a_i
ai为第i幢能看到的塔的编号,那么我们就可以把
a
i
a_i
ai ~
a
i
+
1
−
1
a_{i+1}-1
ai+1−1分为一个区间,因为这个区间只有一个能够被看到,那我们想到了什么呢?什么都没有这不可以用第一类斯特林数么?因为第一类斯特林数是求的把n个点构成m个圆,而这道题一个区间跟圆的性质是相似的,因为这正好就是非空循环排列,第一类斯特林数中提到的名词,每一个不同排列的环,都可以形成唯一一个以最大元素为开头的排列,即把该环从最大元素处打开。 (出自Nature_Ran的博客)所以除了中间最高的塔,其他
n
−
1
n-1
n−1个点可以分为
F
−
1
+
B
−
1
F-1+B-1
F−1+B−1组,然后我们就从这
F
−
1
+
B
−
1
F-1+B-1
F−1+B−1组中选出
F
−
1
F-1
F−1组放左边,所以就是
s
[
N
−
1
]
[
F
+
B
−
2
]
×
C
(
F
+
B
−
2
,
F
−
1
)
s[N-1][F+B-2]\times C(F+B-2,F-1)
s[N−1][F+B−2]×C(F+B−2,F−1)(就是上一句话的数学表示)
代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define Int register int
#define int long long
#define mod 1000000007
#define MAXN 2005
int N,F,B;
int s[MAXN][MAXN],c[MAXN][MAXN];
int inv[MAXN],inv_fac[MAXN],fac[MAXN];
void init()
{
s[0][0] = 1;
c[0][0] = 1;
for (Int i = 1;i <= 2000;++ i)
{
c[i][0] = c[i][i] = 1;
s[i][0] = 0;s[i][i] = 1;
for (Int j = 1;j < i;++ j)
{
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
s[i][j] = (s[i - 1][j - 1] % mod + s[i - 1][j] % mod * (i - 1) % mod) % mod;
}
}
}
int C (int n,int m)
{
return c[n][m];
}
void read (int &x)
{
x = 0;char c = getchar();int f = 1;
while(c < '0' || c > '9'){if(c == '-')f = -f;c = getchar();}
while(c >= '0' && c <= '9'){x = (x << 3) + (x << 1) + c - '0';c = getchar();}
x *= f;return ;
}
void write (int x)
{
if (x < 0){x = -x;putchar('-');}
if (x > 9) write (x / 10);
putchar (x % 10 + '0');
}
signed main()
{
init();
int times;
read (times);
while (times --)
{
read (N),read (F),read (B);
if (F + B - 2 > N) puts ("0");
else write (s[N - 1][F + B - 2] % mod * c[F + B - 2][F - 1] % mod),putchar ('\n');
}
return 0;
}
//s(n,m)=s(n-1,m-1)+s(n-1,m)*(n-1)
//s(n-1,F+B-2)*C(F+B-2,F-1)
//3 2 2