宝石装箱(容斥原理)
传送门
ans=总方案数−不可行的方案数=anssum−ansno
anssum=总方案数
=n!
设集合
dp[i]为选
i个箱子装不合法宝石的方案数,
a[i]为第
i个箱子不能装的宝石数。
类比背包可得递推式:
dp[i]=dp[i]+dp[i−1]×a[i]
令
Ai为至少
i个箱子不合法的方案数。
则
Ai=dp[i]×(n−i)!
则有不可行的方案数
=ansno
=∣A1∪A2∪A3⋯∪An∣
根据容斥原理有:
ansno=k=1∑n(−1)k−1×dp[k]×(k−i)!
即
ans=anssum−ansno =n!−k=1∑n(−1)k−1×dp[k]×(n−k)!
n!=(−1)0×dp[0]×n!
所以
ans=k=0∑n(−1)k×dp[k]×(n−k)!
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=8e3+10,mod=998244353;
#define mst(a) memset(a,0,sizeof a)
int n;
ll dp[N]={1},a[N],f[N]={1};
int main(){
scanf("%d",&n);
for(int i=1,x;i<=n;i++)
scanf("%d",&x),a[x]++,f[i]=f[i-1]*i%mod;
for(int i=1;i<=n;i++)
for(int j=i;j>=0;j--)
dp[j+1]=(dp[j+1]+dp[j]*a[i])%mod;
ll ans=0;
for(int i=0,p=1;i<=n;i++,p*=-1)
ans=(ans+dp[i]*p*f[n-i]%mod)%mod;
ans=(ans+mod)%mod;
printf("%lld\n",ans);
return 0;
}