大致题意: 有(n)种物品,每个单位时间生成一种物品,其中第(i)种物品有(frac{p_i}{sum_{t=1}^np_t})的概率生成。求生成(k)种物品的期望时间。
前言
这道题来自(XZY)神仙的洛谷智推。
能被智推到这种神仙题,充分体现出连人工智能都已经充分认识到(XZY)的神仙本质,在此疯狂膜拜(XZY)神仙(\%\%\%)。
我这个蒟蒻想了快两个小时都没做出来,最后还是去翻了题解,深深感觉到自己的弱小。
(kth Min-Max)容斥
对于这道题,首先你需要知道一个公式,即(kth Min-Max)容斥。
考虑(Min-Max)容斥的公式为:
而(kth Min-Max)容斥的公式就是(Min-Max)容斥公式的推广,两者十分相像:
这个公式是我们做这道题的基础。
同时需要注意,题目中询问生成(k)种物品的期望,并非(kth Max),而是((n-k+1)th Max)(一开始就被这个坑了还自以为想出了正解......)。
为了方便起见,我们可以直接把读入的(k)修改为(n-k+1),则注意题目数据范围里特殊给出的(n-kle10),此时就变成了(kle11)。
动态规划
考虑(E(Min(T)))是个什么东西。
根据其定义,我们知道,就是取到点集(T)中任意一点的期望。
显然,设(sum_T)为点集(T)中所有(p)的总和,则取到点集(T)中任意一点的概率就等于(frac{sum_T}m)。
则期望显然就等于(frac m{sum_T})。
把这个东西代回到式子中,就得到:
然后我们就可以发现,在这个式子中,与(T)有关的项只有(|T|)和(sum_T)。
所以我们似乎可以考虑,进行一个与(|T|)和(sum_T)有关的动态规划。
于是就可以设状态(f_{i,j,t})表示在前(i)种物品中选择若干个物品,并使得(sum_T=t),此时的((-1)^{|T|-j}C_{|T|-1}^{j-1})的值的总和。
这个东西怎么转移呢?
如果我们不选当前物品,显然有(f_{i,j,t}=f_{i-1,j,t})。
如果我们选当前物品,则根据组合数的一个基本公式(C_n^m=C_{n-1}^m+C_{n-1}^{m-1})可得:
也就是:
观察这个式子便可以发现:
于是我们就得到了转移方程(是不是无比玄学......)。
而最后答案显然就是(sum_{t=1}^mf_{n,k,t}cdotfrac mt)。
关于内存
显然这个数组开不下。
发现这个东西跟背包似乎长得有些类似,于是我们可以利用和背包类似的想法,把第一位(i)去掉,而之后两维倒着枚举进行转移。
关于边界
边界的设置似乎十分玄学。
大体就是:
至于为什么,是因为这么设置边界刚好能使得最初的状态正确,可以自己去推一推。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define M 10000
#define K 10
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,k,a[N+5],f[K+5][M+5];
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
I int XSum(CI x,CI y) {return x+y>=X?x+y-X:x+y;}
int main()
{
RI i,j,t,ans=0;for(scanf("%d%d%d",&n,&k,&m),k=n-k+1,i=1;i<=n;++i) scanf("%d",a+i);//读入时把k修改为n-k+1
for(j=1;j<=k;++j) f[j][0]=X-1;for(i=1;i<=n;++i)//设边界
for(j=k;j;--j) for(t=m;t>=a[i];--t) f[j][t]=XSum(f[j][t],XSum(f[j-1][t-a[i]],X-f[j][t-a[i]]));//动态规划转移
for(t=1;t<=m;++t) ans=(1LL*f[k][t]*m%X*Qpow(t,X-2)+ans)%X;return printf("%d",ans),0;//统计答案
}