牛客5633D - 宝石装箱
题意
- 将 n 个物品装进 n 个箱子,每个箱子恰好装一个物品。
- 要求第 i 个物品不能装入第 ai 个箱子中。
- 问有多少种合法的方案。
思路
错误思路
- 线性容斥。计算不合法的总方案数。ans=n!−不合法的总方案数
- 不合法的总方案数=∑i=1n前i个箱子合法且第i个箱子不合法的方案数
- 上面这个表达式是正确的,但是 前i个箱子合法且第i个箱子不合法的方案数 太难算。
正确思路
- 线性容斥。
- 我们发现 至少有 i 个箱子不合法 好算
- 我们从至少有 i 个箱子不合法入手
- dp[i] 表示至少有 i 个箱子不合法
- ans=∑i=1n[(−1)i×dp[i]×(n−i)!]
- 如何计算 dp[i]?
- 背包转移。
代码
#include <cstdio>
#include <iostream>
#define int long long
const int N = 1e6+10;
const int MOD = 998244353;
using namespace std;
int bin[N];
int cnti[N],dp[N];
int n;
void Solve()
{
bin[0] = 1;
for (int i=1; i<=n; i++)
{
bin[i] = bin[i-1]*i%MOD;
}
dp[0] = 1;
for (int i=1; i<=n; i++)
{
if(!cnti[i])continue;
for (int j=n; j>=1; j--)
{
dp[j] += dp[j-1]*cnti[i]%MOD, dp[j]%=MOD;
}
}
int ans = 0;
int base = -1;
for (int i=0; i<=n; i++)
{
base *= -1;
ans += (base * dp[i] * bin[n-i] + MOD) % MOD, ans+=MOD,ans%=MOD;
}
printf("%lld\n",ans);
}
signed main()
{
scanf("%lld",&n);
for (int i=1; i<=n; i++)
{
int x;
scanf("%lld",&x);
cnti[x]++;
}
Solve();
return 0;
}