CF-Round 86-div2-E题

8 篇文章 0 订阅
3 篇文章 0 订阅

CF-Round 86-div2-E题

E. Placing Rooks

传送门

这道题组合 + 乘法逆元 + 容斥问题

容斥:容斥原理,有时候在计数时,情况比较多的情况下,相互之间有重叠。为了使重叠部分不被重复计算,可以这样处理:先不考虑重叠的情况,把所有对象的数目计算出来,然后减去重复计算的数目。

题目大意:给定一个n * n的棋盘,你需要在上面放置n个棋子。
给定n和k;
给定以下规定:
1.每个没有放置棋子的空单元格均需要受到攻击
2.一共恰好有k对棋子相互攻击
空单元都到攻击的条件是:空单元所在行或者所在列上面有棋子。
一对棋子相互攻击的条件是:两个棋子在相同行或者相同列,其中他们之间没有棋子阻隔。

问最终有多少种不同的棋子的摆放方法。答案模998244353。

本题思路:首先我们需要知道当一行或者一列上面有x个棋子的时候,我们就会有(x - 1)对相互攻击的棋子。
要求每个空单元棋子均需要受到攻击。那我们需要让所有行或者所有列上面都要有棋子。
这里我们就让所有行上面都有棋子。
于是我们得到行的情况数以后,我们最后只需要做一个乘2的操作就行了。
如果恰好有k对棋子相互攻击,那么我们需要k+1个棋子。假设我们把这(k + 1)放在同一列上,那么现在占据了一列(一列上面是有棋子的),剩余n - (k + 1) = n - k - 1个棋子,他们之间的列需要互不相同。
所以最后我们占据的总列数:1 + n - k - 1 = n - k 列(n - k列上面有棋子)

现在我们要做的事情就是考虑n个棋子每行上面都需要有一个棋子并且(n - k)个列上面也有棋子的情况有多少种。

设x = n - k;
那么我们在n个列中选x个列的情况是(组合问题):1
上面这个符号我下面就用C(x, n)代替了哈。(贴照片太不好看了)

那我们考虑:x个列上面的总情况数是x^n。一共有n个棋子,每个棋子有x种选择(选择哪一列);
case1: 我们在总情况下减去至少有一列为空的情况:(x - 1)^nC(1, x)
case2: 加上至少有两列为空的情况:(x - 2)^nC(2, x);
这里我们考虑两列为空的时候是第a列为空和第b列为空。
当第a列为空或者第b列为空的时候,我们已经在情况1种减去了。我们这里加上的是第a列为空并且第b列为空的情况。可以获得只有一列为空的情况
case3: 减去至少有三列为空的情况:(x - 3)^nC(3, x);
这里我们考虑三列为空的时候是第p列,第q列,第r列为空。
当p且q为空,q且r为空,p且r为空的时候。就是我们case2加上的情况。这里我们减去三列均为空的情况,即p且q且r为空的情况。可以获得只有两列为空的情况。

按照这样的思路(就是容斥啦~)
一直到x,我们综合起来就可以获得下面的式子:
2
代码部分就是运用组合然后乘法逆元即可~
乘法逆元博客参考:
https://blog.csdn.net/qq_44624316/article/details/105677150
注意要mod哦~
代码部分对(-1)^i的判断(为了不讨论x的奇偶情况)
我们就可以看看i和x的奇偶性是否相同,如果相同那么就是正的,不相同呢,就是负。

我们可以特判以下,如果给的k已经超过n-1了。我们只有n个棋子,这很明显没有结果,直接输出0即可。
然后如果k=0的时候,要求我们没有互相攻击的棋子,那么对于行来说n个棋子的摆放和对于列来说n个棋子的摆放是一样的,所以不需要乘以2.(摆放的形式都是一行和一列上面最多有一个棋子)

嘻嘻,其余的情况最后求出结果乘2就行。

注意取模哦~(做减操作的时候要加上mod,避免减成负数啦)

代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 2e5 + 10;

int fac[N];

int mul(int a, int b)
{
	return (1ll * a * b) % mod;
}

int npow(int a, int n)
{
	int res = 1;
	while (n)
	{
		if (n & 1)
		{
			res = mul(res, a);
		}
		a = mul(a, a);
		n >>= 1;
	}
	return res;
}

int inv(int a)
{
	return npow(a, mod - 2);
}

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

int main()
{
	int n, k;
	cin >> n >> k;
	if (k > n - 1)
	{
		cout << 0 << endl;
		return 0;
	}
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
	{
		fac[i] = mul(fac[i - 1], i);
	}
	ll x = n - k;
	ll ans = 0;
	for (int i = x; i >= 0; i--)
	{
		if ((i & 1) == (x & 1))
		{
			ans = (ans + mul(npow(i, n), C(x, i))) % mod;
		}
		else
		{
			ans = (ans - mul(npow(i, n), C(x, i)) + mod) % mod;
		}
	}
	ans = mul(ans, C(n, x));
	if (k > 0)
	{
		ans = mul(ans, 2);
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值