宝石装箱(容斥原理)

宝石装箱(容斥原理)

传送门

a n s = = a n s s u m a n s n o ans=总方案数-不可行的方案数=ans_{sum}-ans_{no}

a n s s u m = ans_{sum}= 总方案数 = n ! =n!

设集合 d p [ i ] dp[i] 为选 i i 个箱子装不合法宝石的方案数, a [ i ] a[i] 为第 i i 个箱子不能装的宝石数。

类比背包可得递推式: d p [ i ] = d p [ i ] + d p [ i 1 ] × a [ i ] dp[i]=dp[i]+dp[i-1]\times a[i]

A i A_i 为至少 i i 个箱子不合法的方案数。

A i = d p [ i ] × ( n i ) ! A_i=dp[i]\times(n-i)!

则有不可行的方案数 = a n s n o =ans_{no} = A 1 A 2 A 3 A n =|A_1\cup A_2\cup A_3\dots\cup A_n|

根据容斥原理有: a n s n o = k = 1 n ( 1 ) k 1 × d p [ k ] × ( k i ) ! ans_{no}=\sum\limits_{k=1}^n(-1)^{k-1}\times dp[k]\times(k-i)!

a n s = a n s s u m a n s n o   = n ! k = 1 n ( 1 ) k 1 × d p [ k ] × ( n k ) ! ans=ans_{sum}-ans_{no}\ =n!-\sum\limits_{k=1}^n(-1)^{k-1}\times dp[k]\times(n-k)!

n ! = ( 1 ) 0 × d p [ 0 ] × n ! n!=(-1)^0\times dp[0]\times n!

所以 a n s = k = 0 n ( 1 ) k × d p [ k ] × ( n k ) ! ans=\sum\limits_{k=0}^{n}(-1)^k\times dp[k]\times(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;
} 

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/106295461