[CF306C White, Black and White Again] 题解

文章详细解释了一道关于排列组合的编程竞赛题目CF306C,题目的目标是计算在一定数量的好天和坏天中,坏天始终在中间的不同排列方式。利用插板法和组合数学的知识,可以得出O(n)时间复杂度的解决方案。文章提供了预处理组合数和模逆的技巧,并附带了AC代码实现。
摘要由CSDN通过智能技术生成

[CF306C White, Black and White Again] 题解

一篇 O ( n ) O(n) O(n) 的题解,这是一道不戳的组合数学题

“坏天“一定是在中间的,所以我们可以先将“坏天”插到”好天“中。所以我们可以枚举“好天”的个数 i i i,对于 w w w 个“好天”,可以使用《插板法》计算出对于每一种“好天”的排列方式共有 C w − 1 i − 1 C_{w-1}^{i-1} Cw1i1 种方式,“坏天”有 n − i n-i ni 个,对于每种“坏天”的排列方式,共有 C b − 1 n − i − 1 C_{b-1}^{n-i-1} Cb1ni1 种插入方式,而“坏天”有 i − 1 i-1 i1 个空可以插入,所以可以算出来,如果有 i i i 个“好天”,辣么每种好天和坏天的排列就都有 C w − 1 i − 1 × ( i − 1 ) × C b − 1 n − i − 1 C_{w-1}^{i-1}\times(i-1)\times C_{b-1}^{n-i-1} Cw1i1×(i1)×Cb1ni1,最终的答案就是 ∑ i = 2 n − 1 b ! × w ! × C w − 1 i − 1 × ( i − 1 ) × C b − 1 n − i − 1 = b ! × w ! × ∑ i = 2 n − 1 C w − 1 i − 1 × ( i − 1 ) × C b − 1 n − i − 1 \displaystyle \sum_{i=2}^{n-1}{b!\times w!\times C_{w-1}^{i-1}\times(i-1)\times C_{b-1}^{n-i-1}}=b!\times w!\times \sum_{i=2}^{n-1}{C_{w-1}^{i-1}\times(i-1)\times C_{b-1}^{n-i-1}} i=2n1b!×w!×Cw1i1×(i1)×Cb1ni1=b!×w!×i=2n1Cw1i1×(i1)×Cb1ni1 种方式。

我们可以发现柿子中的组合数只涉及 C w − 1 i C_{w-1}^{i} Cw1i C b − 1 i C_{b-1}^{i} Cb1i,可以 O ( n ) O(n) O(n) 预处理!!! C y x = C y x − 1 × ( y − x + 1 ) ÷ x C_y^x=C_y^{x-1}\times (y-x+1) \div x Cyx=Cyx1×(yx+1)÷x 而其中的 ÷ x \div x ÷x 也可以 O ( n ) O(n) O(n) 预处理!!!在膜 p p p 意义下 i n v [ i ] = i n v [ p   m o d   i ] × ( p − p / i ) inv[i]=inv[p \bmod i]\times(p-p/i) inv[i]=inv[pmodi]×(pp/i)

贴一下 A C AC AC 代码

int C1[4010]/*C[w-1][]*/, C2[4010]/*C[b-1][]*/;
int inv[4010];
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, w, b;
	cin >> n >> w >> b;
	inv[1] = 1;
	for (int i = 2; i <= 4000; i++)
		inv[i] = inv[mod % i] * (mod - mod / i) % mod;
	C1[0] = C1[w - 1] = 1;
	for (int i = 1, k1 = w - 1, k2 = 1; i < w - 1; i++, k1--, k2++)
		C1[i] = ((C1[i - 1] * k1) % mod * inv[k2]) % mod;
	C2[0] = C2[b - 1] = 1;
	for (int i = 1, k1 = b - 1, k2 = 1; i < b - 1; i++, k1--, k2++)
		C2[i] = ((C2[i - 1] * k1) % mod * inv[k2]) % mod;
	int ans = 0;
	for (int i = 2; i < n; i++) {
		int j = n - i;
		ans = (ans + ((C1[i - 1] * (i - 1)) % mod * C2[j - 1]) % mod) % mod;
	}
	int k1 = 1;
	for (int i = 1; i <= w; i++)
		k1 = (k1 * i) % mod;
	int k2 = 1;
	for (int i = 1; i <= b; i++)
		k2 = (k2 * i) % mod;
	ans = (ans * k1) % mod;
	ans = (ans * k2) % mod;
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值