洛谷 P4707 重返现世
题面
题目描述
为了打开返回现世的大门,Yopilla 需要制作开启大门的钥匙。Yopilla 所在的迷失大陆有 \(n\) 种原料,只需要集齐任意 \(k\) 种,就可以开始制作。
Yopilla 来到了迷失大陆的核心地域。每个单位时间,这片地域就会随机生成一种原料。每种原料被生成的概率是不同的,第 \(i\) 种原料被生成的概率是 \(\frac{p_i}{m}\) 。如果 Yopilla 没有这种原料,那么就可以进行收集。
Yopilla 急于知道,他收集到任意 \(k\) 种原料的期望时间,答案对 \(998244353\) 取模。
输入输出格式
输入格式:
第一行三个数 \(n, k, m\) 。
第二行 \(n\) 个数 \(p_1, p_2, ..., p_n\)。
输出格式:
输出一行。
输入输出样例
输入样例#1:
3 3 3
1 1 1
输出样例#1:
499122182
说明
对于 \(10 \%\) 的数据,\(p_1 = p_2 = ... = p_m\)。
对于另外 \(10 \%\) 的数据,\(k = n\) 。
对于 \(70 \%\) 的数据,\(n \le 100\) 。
对于 \(100 \%\) 的数据,
\(1 \le n \le 1000\) ,\(1 \le k \le n, \lvert n - k \rvert \le 10\),
\(0 \le p_i \le m, \sum p = m, 1 \le m \le 10000\) 。
题目分析
%%%Trrui大佬,
题目大意:求第\(k\)种原料的期望生成时间。
考虑广义min-max反演:
对于集合\(T\):
我们设\(\text{dp}\)来求出\(\sum p_i\)相等的\(T\)的方案数。
设\(f_{i,j}\)表示\(|T|=i,\sum_{i\in T}p_i=j\)时的方案数,转移十分显然。
时间复杂度为\(O(n^2m)\),可以得到\(70\)分的好成绩。
由于我们读入的\(k\)是第\(k\)小,而反演是用的是第\(k\)大,
所以,以下的\(k\)均为\(n-k+1\)。
考虑改变\(\text{dp}\)状态,
设\(f_{j,k}\)表示当\(\sum p_i=j\),\(k\)为\((-1)^{|T|-k}\binom{|T|-1}{k-1}\)中的\(k\)时,\(\sum_{T}(-1)^{|T|-k}\binom{|T|-1}{k-1}\)的值。
对于一个\(p_i=v\)的新物品,考虑两种转移:
如果不加入,则\(f_{j,k}=f'_{j,k}\)。
如果加入,我们需要用到一个trick:(Trrui大佬教我的)
由于\(k\)表示组合数,所以\(f_{j,k}\)与\(f'_{j-v,k-1}\)相关。
设\(g_{i,j}\)表示\(|T|=i,\sum_{i\in T}p_i=j\)时的方案数,则
如果新增一个物品,则新的\(g_{i+1,j}=g_{i,j-v}\),对于转移前的\(g\)而言:
两式相减,
由此可得:
\(DP\)求解即可,时间复杂度\(O((n-k)\cdot m)\)。
最终统计答案:
考虑如何附初值:
对于初始情况而言:
\(f_{j,k}\)表示\(|T|=0,\sum p=j,组合数为k\)时的值,
由于没有\(i\),因此只有\(j=0\)有意义,我们只需处理\(f_{0,k}\)的初值。
根据广义二项式定理,
代码实现
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#define MAXN 0x7fffffff
typedef long long LL;
const int N=1005,mod=998244353;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
int ksm(int x,int k){
int ret=1;
while(k){
if(k&1)ret=(LL)ret*x%mod;
x=(LL)x*x%mod,k>>=1;
}
return ret;
}
int p[N],f[2][10005][15],ans;
int main(){
int n=Getint(),K=n-Getint()+1,m=Getint();
for(int i=1;i<=n;i++)p[i]=Getint();
for(int i=1;i<=10;i++)f[0][0][i]=mod-1;
int t=0;
for(int i=1;i<=n;i++){
t^=1,memset(f[t],0,sizeof(f[t]));
for(int j=0;j<=m;j++){
for(int k=1;k<=K;k++){
f[t][j][k]=f[t^1][j][k];
if(j>=p[i]){
(f[t][j][k]+=mod-f[t^1][j-p[i]][k])%=mod;
(f[t][j][k]+=f[t^1][j-p[i]][k-1])%=mod;
}
}
}
}
for(int i=1;i<=m;i++)
(ans+=(LL)m*ksm(i,mod-2)%mod*f[t][i][K]%mod)%=mod;
cout<<ans;
return 0;
}

浙公网安备 33010602011771号