宝石装箱
这个题容斥应该都很好想到,如果没有限制的话,那么一对一,答案全排列,之后减去第
i
i
i个箱子放错的情况,之后又得加上两个箱子放错的情况,
…
\dots
…,那么后面这个答案如何维护呢,我们设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为当前到了第
i
i
i个箱子,此时已经放错了
j
j
j个的方案数,那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
1
]
∗
a
[
i
]
f[i][j]=f[i-1][j]+f[i-1][j-1]*a[i]
f[i][j]=f[i−1][j]+f[i−1][j−1]∗a[i]
,考虑用一维优化下,二维容易爆空间,得到
d
p
[
1
]
,
d
p
[
2
]
,
…
dp[1],dp[2], \dots
dp[1],dp[2],…然后把之后的每种方案对应乘上其他宝石的全排列即可,最后就是容斥下得到答案。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define rush() int T;scanf("%d",&T);while(T--)
#define mm(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define sc(a) scanf("%d",&a)
#define sc2(a,b) scanf("%d%d",&a,&b)
#define pf(a) printf("%d\n",a)
#define pf2(a,b) printf("%d %d\n",a,b)
#define p_f(a) printf("%d ",a)
#define pyn(a) if(a)puts("Yes");else puts("No");
#define fi first
#define se second
#define db double
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const db eps=1e-9;
const int N=8e3+5;
const int P=998244353;
int n,x,a[N];
ll dp[N],fac[N];
int32_t main()
{
cin>>n;
fac[0]=fac[1]=1ll;
for(int i=2;i<=n;i++)fac[i]=(i*fac[i-1])%P;
ll ans=fac[n];
for(int i=1;i<=n;a[x]++,i++)cin>>x;
dp[0]=1;
for(int i=1;i<=n;i++)for(int j=i;j>=1;j--)dp[j]=(dp[j]+a[i]*dp[j-1])%P;
for(int i=1;i<=n;i++)ans=i&1?(ans-dp[i]*fac[n-i]%P+P)%P:(ans+dp[i]*fac[n-i]%P)%P;
return cout<<ans,0;
}