Placing Rooks(2300/容斥/组合)

题目
官方&代码
题意:给定 n ∗ n n*n nn棋盘,放置 n n n个骑士,要求:1、每个空位置都要与至少一位骑士同一行或同一列。2、刚好有 k k k对骑士产生行或列的碰撞。一个骑士,可以通过走他所在行,或者所在列,走到另一个骑士,中间无其他骑士障碍,那么这2个骑士属于一对碰撞。
思路:由于每个位置都要给骑士访问到,那么这 n n n个骑士要刚好占据 n n n行,或者占据 n n n列,这2个情况等效,我们只需讨论其一,最后答案再 ∗ 2 *2 2即可,但注意当 k = 0 k=0 k=0时不需 ∗ 2 *2 2
n n n个骑士占据了 n n n行,要产生 k k k对碰撞,画图看看,容易发现,要求刚好有 n − k n-k nk个空列,即 n n n个骑士在 k k k列中。这是个排列组合问题。
n n n列中选取出 c = n − k c=n-k c=nk列出来,对于这 c c c列,共有 c n c^n cn种选择,但要减去有一个空列的情况 ( c − 1 ) n ∗ C c 1 (c-1)^n*C_{c}^1 (c1)nCc1,加上有2个空列的情况 ( c − 2 ) n ∗ C c 2 (c-2)^n*C_{c}^2 (c2)nCc2…,容斥下去,答案为 ∑ i = 0 c ( − 1 ) i ∗ ( c − i ) n ∗ C c i \sum_{i=0}^c(-1)^i*(c-i)^n*C_c^i i=0c(1)i(ci)nCci
代码源自官方题解

#include<bits/stdc++.h>

using namespace std;

const int MOD = 998244353;
const int N = 200043;

int add(int x, int y)
{
	return (x + y) % MOD;
}

int sub(int x, int y)
{
	return add(x, MOD - y);
}

int mul(int x, int y)
{
	return (x * 1ll * y) % MOD;
}

int binpow(int x, int y)
{
	int z = 1;
	while(y > 0)
	{
		if(y % 2 == 1)
			z = mul(z, x);
		x = mul(x, x);
		y /= 2;
	}
	return z;
}

int inv(int x)
{
	return binpow(x, MOD - 2);
}

int fact[N];

int C(int n, int k)
{
	return mul(fact[n], inv(mul(fact[k], fact[n - k])));
}

int main()
{
	int n, k;
	cin >> n >> k;
	
	if(k > n - 1)
	{
		cout << 0 << endl;
		return 0;
	}
	
	fact[0] = 1;
	for(int i = 1; i <= n; i++)
		fact[i] = mul(i, fact[i - 1]);
	int ans = 0;
	int c = n - k;
	for(int i = c; i >= 0; i--)
		if(i % 2 == c % 2)
			ans = add(ans, mul(binpow(i, n), C(c, i)));
		else
			ans = sub(ans, mul(binpow(i, n), C(c, i)));
	ans = mul(ans, C(n, c));
	if(k > 0)
		ans = mul(ans, 2);
	cout << ans << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值