【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;
}
标签:04,Contest,省选,ll,位置,1000001,mm,int,inv 来源: https://www.cnblogs.com/Sakura-TJH/p/15925735.html