HDU4372 Count the Buildings (翻译+题解:斯特林数)

题面

(笔者翻译)

There are N buildings standing in a straight line in the City, numbered from 1 to N. The heights of all the buildings are distinct and between 1 and N. 

有n座高楼,从左往右数第 i 座楼的高度为 ai,每座高楼高度互不相同,且 1 ≤ ai ≤ n ,换言之,n座高楼高度形成的序列为1~n的一个排列。

You can see F buildings when you standing in front of the first building and looking forward, and B buildings when you are behind the last building and looking backward. A building can be seen if the building is higher than any building between you and it.

如果有平行光从最左端往右照,那么光能照到的高楼数为 F,如果有平行光从最右端往左照,那么光能照到的高楼数为 B,

(上图为F = 3,B = 2的一个例子) 

Now, given N, F, B, your task is to figure out how many ways all the buildings can be.

告诉你n,F和B,让你求符合n、F 、B条件的合法序列有多少个

Input

First line of the input is a single integer T (T<=100000), indicating there are T test cases followed.
Next T lines, each line consists of three integer N, F, B, (0<N, F, B<=2000) described above.

Output

For each case, you should output the number of ways mod 1000000007(1e9+7).

有最多1e5个数据,n、F、B都是小于等于2000的正整数,最终答案要取模 1e9+7。

题解

既然每个高度不相同,那么对F和B有重复贡献的有且仅有最高的那座楼,

最高的楼左边有F-1个递增排列的楼,每座楼到右边那座比它高的楼中间都有一堆小弟(比它小),

最高的楼右边有B-1个递减排列的楼,每座楼到左边那座比它高的楼中间也有一堆小弟(比它小),

那么说,除了最高的楼之外,其他的楼共有F+B-2个团体,每个团体有一座最高的楼(要算贡献的楼)和一堆小弟(可以没有),

从中选F-1个团体到最大楼的左边(组合数C(F+B-2 , F-1)),他们之间的顺序就确定了(递增),然后小弟们就在对应老大右边乱排列,

剩下的B-1个团体到最大楼的右边(加起来正好 F+B-2 个),他们之间的顺序也确定了(递减),然后小弟们就在对应老大左边乱排列,

想好了放团体的方案数后,我们来想安排团体内部的方案

对于每个团体,把最大的一个拉入队首,剩下的随便排,

这不就是圆排列吗?相关于把每个圈最大的一个转到第一个。

所以,分团体以及安排团体内部的顺序的方案数就是 s(n-1,F+B-2) (第一类斯特林数

把s(n-1,F+B-2)和C(F+B-2 , F-1)乘起来完事,

注意T很大,因此要n^2预处理s和C,然后判断无解的情况。

CODE

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#define MAXN 2005
#define MAXM 35
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x)&(x))
//#define int LL
using namespace std;
inline LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + (s - '0');s = getchar();}
	return x * f;
}
const int jzm = 1000000007;
int n,m,i,j,o,k;
int s[MAXN][MAXN],C[MAXN][MAXN];
int main() {
	s[0][0] = 1;
	C[0][0] = 1;
	for(int i = 1;i <= 2000;i ++) {
		C[i][0] = 1;
		for(int j = 1;j <= i;j ++) {
			s[i][j] = (s[i-1][j-1] +0ll+ (i-1ll) *1ll* s[i-1][j] % jzm) % jzm;
			C[i][j] = (C[i-1][j-1] +0ll+ C[i-1][j]) % jzm;
		}
	}
	int T = read();
	while(T --) {
		n = read();
		int F = read(),B = read();
		if(F+B-1 > n || max(F,B) <= 1) {
			printf("0\n");
			continue;
		}
		int ans = s[n-1][F+B-2] *1ll* C[F+B-2][F-1] % jzm;
		printf("%d\n",ans);
	}
	return 0;
}

第一类斯特林数

简单解释一下

想增进了解的点这里

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值