P4707 重返现世 题解
Statement
P4707 重返现世 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Solution
考虑 min-max 容斥,题目所求即 \(\min_\limits{k}(U)\)
知道 \(k\) 较大但 \(n-k\) 很小,所以考虑转化一下,知道 \(\min_\limits k(U)=\max_\limits {n+1-k}(U)\) ,下面的所有 \(k\) 都是 \(n+1-k\) 的意思,直接套用 min-max 容斥的公式:
由几何分布,知道 \(E(\min(T))=\dfrac 1{\sum_{i\in T}p_i}\) ,其中 \(p_i\) 表示 \(i\) 物品出现概率
这样直接做的时间复杂度是 \(O(2^n)\) 左右的,显然要 G,但是容易发现其实很多的 \(E(min(T))\) 的值域 \(m\) 比较小,所以我们的一个重要优化思路 dp 出现了,其主旨是求出对于每一个 \(\min(T)\) 而言,他前面的系数和。
设 \(f_{i,j,k}\) 表示前 \(i\) 个物品,所有集合 \(T\) 满足 \(\sum_{x\in T} p_x=j\) ,确定式子中 \(k\) 值的时候的 \(\sum(-1)^{|T|-k}\binom{|T|-1}{k-1}\) 的值
我们的转移类似背包,如若第 \(i\) 个物品不选,那么 \(f_{i,j,k}=f_{i-1,j,k}\)
如若第 \(i\) 个物品选了,因为这个时候我们往集合 \(T\) 里面塞了一个 \(i\) 进去,理应从 $f_{i-1,j-p_i} $ 转过来,同时因为 \(|T|\) 增加 \(1\) ,所以 \((-1)^{|T|-k}\) 变号,又有 \(\binom{|T|}{k-1}=\binom{|T|-1}{k-1}+\binom{|T|-1}{k-2}\)
知道 \(f_{i,j,k}=f_{i-1,j-p,k-1}-f_{i-1,j-p,k}\) (注意到 \(k-1\) 的时候,\((-1)^{|T|-k}\) 再次变号)
转移即可,时间复杂度 \(O(nmk)\) ,空间复杂度滚掉第一维后 \(O(mk)\),答案即为 \(\sum_i \dfrac {f_{n,i,k}}i\)
初值 \(f_{0,0,0}=(-1)^0\binom{-1}{-1}=1\) 即可
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3+5;
const int M = 1e4+5;
const int mod = 998244353;
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int p[N],dp[M][15],inv[M];
int n,m,K;
int ksm(int a,int b){
int res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod,b>>=1;
}
return res;
}
signed main(){
n=read(),K=n-read()+1,m=read();
for(int i=1;i<=n;++i)p[i]=read(); inv[0]=dp[0][0]=1;
for(int i=1;i<=m;++i)inv[i]=ksm(i,mod-2);
for(int i=1;i<=n;++i)
for(int j=m;j>=p[i];--j)
for(int k=K;k;--k)
(dp[j][k]+=(dp[j-p[i]][k-1]-dp[j-p[i]][k]+mod)%mod)%=mod;
int ans=0;
for(int i=0;i<=m;++i)(ans+=dp[i][K]*inv[i]%mod)%=mod;
printf("%lld\n",ans*m%mod);
return 0;
}