[FJOI2016]建筑师

洛谷题面

第一次见到第一类 \(\rm stirling\) 数的运用,还是比较巧妙的。

题目大意

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

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

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

题目分析

借用一下 @zyzzyzzyzzyz 大佬的图:

定义每个红框框为一群建筑,一群建筑中最高的建筑为这群建筑的代表。例如:\(1,2,3\) 号建筑属于一群建筑,\(1\) 号是这一群建筑的代表。

考虑每一群建筑:只要确定了最高的建筑(即这群建筑的代表),那么剩下的建筑就可以随便排列了,方案数为 \((k-1)!\)\(k\) 为该群建筑的建筑个数。发现 \((k-1)!\) 和圆排列类似,也就是这 \(k\) 个建筑的圆排列数。不懂的戳这里 \(\color{red}\verb!link!\)

那么除去整个序列中最高的建筑,从左看有 \(a-1\) 个,从右看有 \(b-1\) 个,即 \(a-1+b-1=a+b-2\) 个圆排列。方案数为 \([^{n-1}_{a+b-2}]\)\([^a_b]\) 为第一类 \(\rm stirling\) 数。不懂的戳这里 \(\color{red}\verb!link!\)

在总共 \(a+b-2\) 群建筑中选 \(a-1\) 群放在最高建筑的左边,有 \(a+b-2\choose a-1\) 种。

根据乘法原理,方案数为 \([^{n-1}_{a+b-2}]\times{a+b-2\choose a-1}\)

代码

//2022/4/11
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#define int long long
#define enter putchar(10)
#define debug(c,que) cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) cout << arr[i] << w;
#define speed_up() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define Abs(x) ((x) > 0 ? (x) : -(x))
#define stop return(0)
const int mod = 1e9 + 7;
inline int MOD(int x) {
	if(x < 0) x += mod;
	return x % mod;
}
namespace Newstd {
	char buf[1 << 21],*p1 = buf,*p2 = buf;
	inline int getc() {
		return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
	}
	inline int read() {
		int ret = 0,f = 0;char ch = getc();
		while (!isdigit(ch)) {
			if(ch == '-') f = 1;
			ch = getc();
		}
		while (isdigit(ch)) {
			ret = (ret << 3) + (ret << 1) + ch - 48;
			ch = getc();
		}
		return f ? -ret : ret;
	}
	inline void write(int x) {
		if(x < 0) {
			putchar('-');
			x = -x;
		}
		if(x > 9) write(x / 10);
		putchar(x % 10 + '0');
	}
}
using namespace Newstd;
using namespace std;

const int N = 5e4 + 5,M = 205;
int C[M][M],s1[N][M];
int T,n,a,b;
inline void init() {
	C[0][0] = 1;
	for (register int i = 1;i <= 200; ++ i) {
		C[i][0] = 1;
		for (register int j = 1;j <= i; ++ j) {
			C[i][j] = MOD(C[i - 1][j] + C[i - 1][j - 1]);
		}
	}
	s1[0][0] = 1;
	for (register int i = 1;i <= 5e4; ++ i) {
		for (register int j = 1;j <= 200; ++ j) {
			s1[i][j] = MOD(s1[i - 1][j - 1] + MOD((i - 1) * s1[i - 1][j]));
		}
	}
}
inline void solve() {
	n = read(),a = read(),b = read();
	printf("%lld\n",MOD(s1[n - 1][a + b - 2] * C[a + b - 2][a - 1]));
}
#undef int
int main(void) {
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	#define int long long
	init();
	T = read();
	while (T --) solve();

	return 0;
}
posted @ 2022-04-11 22:37  Coros_Trusds  阅读(26)  评论(0编辑  收藏  举报