【2022 省选训练赛 Contest 04 A】permutation(容斥)

permutation

题目链接:2022 省选训练赛 Contest 04 A

题目大意

给你一个排列,然后有一些位置告诉你了。
然后问你有多少种可能使得每个位置的数都不是它位置的编号。

思路

不难看出就一个限制条件 \(p_i\neq i\),我们可以容斥,每次是至少有 \(x\) 个位置出现了 \(p_i=i\) 的情况。

然后你可以预先搞出有多少个空位 \(m\),以及有多少个数是肯定不能放回自己的位置 \(mm\)(自己的位置上有数或者自己已经放在了别的位置)。

然后就是:
\(\sum\limits_{i=0}^{mm}-1^{i}C_{i}^{mm}(m-i)!\)
(组合数就是选那些位置放 \(p_i=i\),阶乘就是后面的随便放)

代码

#include<cstdio>
#define ll long long
#define mo 998244353

using namespace std;

int n, a[1000001], m, mm;
ll ans, jc[1000001], inv[1000001];
bool cant[1000001];

ll C(ll n, ll m) {
	return jc[n] * inv[m] % mo * inv[n - m] % mo;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		if (a[i] == i) {
			printf("0"); return 0;
		}
		if (!a[i]) m++;//数空的位置数
		if (a[i]) cant[a[i]] = cant[i] = 1;//数位置或者数被用了的位置数
	}
	for (int i = 1; i <= n; i++) if (!cant[i]) mm++;
	
	jc[0] = 1; for (int i = 1; i <= m; i++) jc[i] = jc[i - 1] * i % mo;
	inv[0] = inv[1] = 1; for (int i = 2; i <= m; i++) inv[i] = inv[mo % i] * (mo - mo / i) % mo;
	for (int i= 1; i <= m; i++) inv[i] = inv[i - 1] * inv[i] % mo;
	
	ll op = 1;
	for (int i = 0; i <= mm; i++, op = mo - op) {
		(ans += op * C(mm, i) % mo * jc[m - i] % mo) %= mo;
	}
	printf("%lld", ans);
	
	return 0;
}
posted @ 2022-02-23 07:30  あおいSakura  阅读(84)  评论(0编辑  收藏  举报