[luogu4707]重返现世——min-max容斥拓展+动态规划
题目大意:
给定\(n\)个物品和每个物品出现的概率,收集到至少\(k\)个物品的期望时间。
\(k \leq 10\)
思路:
好题!
容斥计算第k大的期望,考虑计算第i大的数的贡献:
\[\begin{aligned}
&\sum_{j=0}^{i-1}{i-1\choose j}f_{j}=[i=k]\\
&\sum_{j=0}^{i}{i\choose j}f_{j}=[i=k-1]\\
&f_i=\sum_{j=0}^{i}(-1)^{i-j}{i\choose j}[j=k-1]\\
&f_i=(-1)^{i-k+1}{i\choose k-1}\\
\end{aligned}
\]
可得kth-max 的公式为:
\[kth(S)=\sum_{T\subseteq S}(-1)^{|T|-k}{|T|-1\choose k-1}\min(T)
\]
其中\(\min(T)=\frac{m}{\sum_{i\in T}p_i}\)。
我们需要求出所有\(|T|\geq k\)的\(T\),无法直接全部求出,观察到\(m=\sum{p_i} \leq 10^{4}\),于是考虑DP。
设\(f_{i,j}\)为选了\(i\)个数,其中\(\sum{p_i}=j\)的方案数,时间复杂度\(\Theta(n^2m)\)。
时间复杂度难以通过,考虑进一步优化:
设\(dp_{j,k}\) 表示\(\sum{p_i}=j\),且当前求的是第\(k\)大的方案数系数之积的和,考虑依次将\(n\)个物品加入,对于\(dp_{j,k}\),有加入第\(i\)个物品和不加入第\(i\)个物品两种选择,转移时主要考虑强制加入第\(i\)种物品的选择。
对于强制选择第i个点的部分,集合的大小需要加\(1\),考虑从前\(i-1\)个物品的状态转移过来,不难发现我们需要的式子是:
\[\sum_{i}(-1)^{i+1-k}{i \choose k-1}f_{i,j-p}
\]
其中 i 枚举的是之前选择的集合大小。
但是上一个状态\(dp_{j-p,k-1}\)可以给我们提供的式子是:
\[\sum_{i}(-1)^{i-k+1}{i-1 \choose k-2}f_{i,j-p}
\]
不难发现之中我们所缺失的部分为:
\[\begin{aligned}
&\sum_{i}(-1)^{i-k+1}{i-1\choose k-1}f_{i,j-p}\\
=&-\sum_{i}(-1)^{i-k}{i-1\choose k-1}f_{i,j-p}\\
=&-dp_{j-p,k}
\end{aligned}
\]
于是到了最后我们得到了转移方程:
\[dp_{j,k}=dp'_{j,k}+dp'_{j-p,k-1}-dp'_{j-p,k}\\
\]
/*=======================================
* Author : ylsoi
* Time : 2019.1.16
* Problem : luogu4707
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("luogu4707.in","r",stdin);
freopen("luogu4707.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=1000+10;
const int maxm=10000+10;
const int mod=998244353;
int n,k,m,p[maxn];
ll fac[maxn],ifac[maxn];
ll dp[maxm][maxn],ans;
ll qpow(ll x,ll y){
ll ret=1; x%=mod;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
void math_init(){
fac[0]=1;
REP(i,1,1e3)fac[i]=fac[i-1]*i%mod;
ifac[1000]=qpow(fac[1000],mod-2);
DREP(i,1e3-1,0)ifac[i]=ifac[i+1]*(i+1)%mod;
}
ll C(int x,int y){
if(x<0 || y<0 || x<y)return 0;
return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
void init(){
read(n),read(k),read(m);
REP(i,1,n)read(p[i]);
}
void ad(ll &_,ll __){_=(_+__)%mod;}
void work(){
k=n-k+1;
dp[0][0]=1;
REP(i,1,n){
DREP(j,m,p[i]){
DREP(l,k,1){
ad(dp[j][l],dp[j-p[i]][l-1]-dp[j-p[i]][l]);
}
}
}
REP(i,1,m)ad(ans,m*qpow(i,mod-2)%mod*dp[i][k]%mod);
printf("%lld\n",(ans+mod)%mod);
}
int main(){
//File();
math_init();
init();
work();
return 0;
}