【洛谷P4707】重返现世
题目
题目链接:https://www.luogu.com.cn/problem/P4707
为了打开返回现世的大门,Yopilla 需要制作开启大门的钥匙。Yopilla 所在的迷失大陆有 \(n\) 种原料,只需要集齐任意 \(k\) 种,就可以开始制作。
Yopilla 来到了迷失大陆的核心地域。每个单位时间,这片地域就会随机生成一种原料。每种原料被生成的概率是不同的,第 \(i\) 种原料被生成的概率是 \(\frac{p_i}{m}\) 。如果 Yopilla 没有这种原料,那么就可以进行收集。
Yopilla 急于知道,他收集到任意 \(k\) 种原料的期望时间,答案对 \(998244353\) 取模。
思路
首先令 \(k=n-k+1\)。然后考虑扩展 min-max 容斥。
并不是很清楚为什么可以用 min-max 容斥反正题解说可以可以吧(
考虑 dp。设 \(f_{i,k,j}\) 表示前 \(i\) 种原料中,收集到 \(k\) 种原料,\(\sum_{\text{收集了}l}p_l=j\) 时,\(\sum_{T\in S}(-1)^{|T|-k}\binom{|T|-1}{k-1}\) 的值。
考虑如何转移。如果第 \(i\) 个原料没有收集,那么 \(f_{i,k,j}\gets f_{i-1,k,j}\)。
如果第 \(i\) 个原料收集了,那么我们需要知道 \(\sum_{T\in S}(-1)^{|T|-k}\binom{|T|-1}{k-1}\) 的值。但是其中 \(k\) 和 \(|T|\) 都发生了变化,并不是很容易做。
但是我们发现 \(-1\) 的次幂那里只需要讨论符号,所以考虑如何快速计算 \(\binom{|T|-1}{k-1}\)。
根据组合数的递推式
所以我们只需要知道 \(\sum_{T\in S}(-1)^{(|T|-1)-(k-1)}\binom{|T|-2}{k-1}\) 和 \(\sum_{T\in S}(-1)^{(|T|-2)-(k-2)}\binom{|T|-2}{k-2}\) 即可。也就是 \(f_{i-1,k,j-p_i}\) 和 \(f_{i-1,k-1,j-p_i}\)。不难发现,这两项的系数分别为 \(-1\) 和 \(1\)。
所以
然后 dp 即可。空间无法接受就直接滚动。
最后枚举 \(j\) 计算答案即可。
时间复杂度 \(O(nm(n-k))\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int K=12,M=10010,MOD=998244353;
int n,m,cnt;
ll ans,p[M],f[2][K][M];
ll fpow(ll x,ll k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%MOD)
if (k&1) ans=ans*x%MOD;
return ans;
}
int main()
{
scanf("%d%d%d",&n,&cnt,&m);
cnt=n-cnt+1;
for (int i=1;i<=n;i++)
scanf("%lld",&p[i]);
for (int k=1;k<=cnt;k++)
f[0][k][0]=MOD-1;
for (int i=1;i<=n;i++)
{
int id=(i&1);
memset(f[id],0,sizeof(f[id]));
for (int k=0;k<=cnt;k++)
for (int j=0;j<=m;j++)
{
f[id][k][j]=f[id^1][k][j];
if (k && j>=p[i]) f[id][k][j]=(f[id][k][j]-f[id^1][k][j-p[i]]+f[id^1][k-1][j-p[i]])%MOD;
}
}
for (int i=0;i<=m;i++)
ans=(ans+f[n&1][cnt][i]*fpow(i,MOD-2))%MOD;
printf("%lld\n",(ans*m%MOD+MOD)%MOD);
return 0;
}