【容斥原理经典题】
题意
第 i i i个物品不能放在 a i a_i ai箱子里,求每个箱子有一个物品的方案数
题解
经典题目
之前做了一道题,需要
c
r
t
crt
crt合并(此题)
也是要考虑到容斥,那题处理方式是利用 d p dp dp,因为有个类似上升子序列的递推关系, d p [ i ] dp[i] dp[i]表示第一个不合法的情况,之后无论怎么选都还是不合法。
此题类似,
g
(
n
)
g(n)
g(n)表示有
n
n
n个不合法的方案数,
f
(
n
)
f(n)
f(n)表示任意排列的方案数。
显然如果有
k
k
k个不合法,方案数显然为
G
(
k
)
=
g
(
k
)
∗
f
(
n
−
k
)
G(k)=g(k)*f(n-k)
G(k)=g(k)∗f(n−k),比赛的时候没考虑到任意排列,如果不任意的话是不能够这么乘的,只有任意的时候才不会被影响。
不过显然,这样子处理的时候,可能会有多于
k
k
k个不合法,所以我们定义
G
(
k
)
G(k)
G(k)为至少
k
k
k个不合法。
根据容斥原理,答案为 ∑ i = 0 n ( − 1 ) i G ( i ) \sum\limits_{i=0}^n (-1)^iG(i) i=0∑n(−1)iG(i)(至少有 i i i个不合法的情况,显然包含 G ( i + 1 ) G(i+1) G(i+1),符合容斥原理的要求)。
最后求
g
(
k
)
g(k)
g(k)的时候,令
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示考虑前
i
i
i个物品不合法,有
j
j
j个不合法;
则有:
g
[
i
]
[
j
]
=
g
[
i
−
1
]
[
j
]
+
g
[
i
−
1
]
[
j
−
1
]
∗
b
[
i
]
g[i][j]=g[i-1][j]+g[i-1][j-1]*b[i]
g[i][j]=g[i−1][j]+g[i−1][j−1]∗b[i],
b
[
i
]
b[i]
b[i]表示要使当前盒子不合法,有几种方法(不能放在该盒子的有几个)。
因为数据较大,需要滚动数组,或者用类似背包逆推的方法。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn = 3e5+10;
const int maxm = 1e6+10;
const int mod = 998244353;
ll f[maxn],g[maxn];
ll a[maxn],x;
int main(){
int n;cin>>n;
f[0]=1;
FOR(i,1,n)f[i]=f[i-1]*i%mod;
g[0]=1;
FOR(i,1,n)cin>>x,a[x]++;
FOR(i,1,n){
for(int j=n;j>=1;j--){
g[j]=(g[j]+g[j-1]*a[i])%mod;
}
}
ll ans=0;
for(int i=0,j=1;i<=n;i++,j*=-1)ans=(ans+1ll*j*g[i]*f[n-i])%mod;
cout<<(ans+mod)%mod<<endl;
}