[洛谷P4609][FJOI2016]建筑师

【算法分析】

  • 首先,考虑最高的建筑 n n n,它一定是从左边看到的最后一个,也是从右边看到的最后一个
  • 剩下左边看到的 A − 1 A-1 A1和右边看到的 B − 1 B-1 B1
  • 考虑将剩下的 n − 1 n-1 n1个建筑分成 A + B − 2 A+B-2 A+B2个集合
  • 从中选出 A − 1 A-1 A1个集合,将这些集合中的建筑放在 n n n的左边,剩下的放右边
  • A − 1 A-1 A1个集合中,同一集合中的建筑在数轴上必为连续的一段
  • 记每个集合中最高的建筑(就是从左边看到的建筑)分别为 c 1 , c 2 , . . . , c A − 1 c_1,c_2,...,c_{A-1} c1,c2,...,cA1,那么这 A − 1 A-1 A1个建筑均位于每一段的最左边
  • 又因为它们都是被看见的(即不能相互遮挡)
  • 所以这 A − 1 A-1 A1段按照 c c c升序从左往右摆放
  • 右边的 B − 1 B-1 B1段同理
  • 答案即 S ( n − 1 , A + B − 2 ) ∗ C ( A + B − 2 , A − 1 ) S(n-1,A+B-2)*C(A+B-2,A-1) S(n1,A+B2)C(A+B2,A1)
  • 其中 S S S是第一类斯特林数,表示 n − 1 n-1 n1个数分成 A + B − 2 A+B-2 A+B2个圆排列(无序)的方案数(因为确定了每段的开头是最高的建筑,圆排列也可以看作固定了开头的排列)

【参考程序】

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

const int mod = 1e9 + 7, e = 50005, o = 105;
int s[e][o * 2], a, b, n, c[o * 2][o * 2], tst;

int main()
{
	int i, j, k;
	s[0][0] = c[0][0] = s[1][1] = 1; 
	for (i = 2; i <= 50000; i++)
	for (j = 1, k = min(i, 200); j <= k; j++)
	s[i][j] = (s[i - 1][j - 1] + (ll)s[i - 1][j] * (i - 1)) % mod;
	for (i = 1; i <= 200; i++)
	for (j = 0; j <= i; j++)
	if (j == 0) c[i][j] = 1;
	else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	read(tst);
	while (tst--)
	{
		read(n);
		read(a);
		read(b);
		int ans = (ll)s[n - 1][a + b - 2] * c[a + b - 2][a - 1] % mod;
		printf("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值