[洛谷P4609] FJOI2016 建筑师

问题描述

小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 n 个建筑,每个建筑的高度是 1 到 n 之间的一个整数。

小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 A 个建筑,从最右边(所有建筑都在左边)看能看到 B 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?

如果建筑 i 的左(右)边没有任何建造比它高,则建筑 i 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。

输入格式

第一行一个整数 T,代表 T 组数据。 接下来 T 行,每行三个整数 n,A,B。

输出格式

对于每组数据输出一行答案 ( ext{mod } 10^9+7)

样例输入

2
3 2 2
3 1 2

样例输出

2
1

数据范围

对于 (10 \%) 的数据 : (1 leq n leq 10)

对于 (20 \%) 的数据 : (1 leq n leq 100)

对于 (40 \%) 的数据 : (1 leq n leq 50000, 1 leq T leq 5)

对于 (100 \%) 的数据 :(1 leq n leq 50000, 1 leq A, B leq 100, 1 leq T leq 200000)

解析

通过题目的描述,我们可以发现,它的意思大概就是这个样子:

对于每一个可以看到的建筑物,他的后面都会有比自己要矮的建筑(或者没有)。按照这个方法来分块,假设某个块的大小为 (x) ,由于块中最高的一定在最外面(不然会看到其他的),这个块的排列方案数就是((x-1)!)。由组合意义,相当于不算最高的,在(n-1)个数中划分成(A+B-2)组,每组进行圆排列。这和第一类斯特林数的形式是一样的。然后,我们要在(A+B-2)组中选择(A-1)组放在最高建筑的左边,其余的放在右边。由于要满足要求,这左边相当于自动排序了,不需要计算排列数。所以,最后的答案就是:

[egin{bmatrix}n-1 \ A+B-2end{bmatrix} imes inom{A+B-2}{A-1} ]

代码

#include <iostream>
#include <cstdio>
#define int long long
#define N 50002
#define M 202
using namespace std;
const int mod=1000000007;
int t,n,a,b,i,j,S[N][M],C[M][M];
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
signed main()
{
	t=read();
	S[0][0]=1;
	for(i=1;i<=50000;i++){
		for(j=1;j<=200;j++) S[i][j]=(S[i-1][j-1]+(i-1)*S[i-1][j]%mod)%mod;
	}
	C[0][0]=1;
	for(i=1;i<=200;i++){
		C[i][0]=1;
		for(j=1;j<=200;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
	while(t--){
		n=read();a=read();b=read();
		printf("%lld
",S[n-1][a+b-2]*C[a+b-2][a-1]%mod);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/12204093.html