1 条题解
-
0
自动搬运
来自洛谷,原作者为

MKCCT
**搬运于
2025-08-24 21:46:58,当前版本为作者最后更新于2020-08-07 18:34:09,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
太菜了,大佬的题解都解释得比较简单,不太能看懂,于是就来写一篇友好一点的。
由于题面实在是太阅读理解了,我们先来提炼一个形式化的表述:给定 种价值不同的元素,你需要维护一个可重集(开始是空集)。每一步你有 的概率删除集合中一个价值最小的元素,如果当前是空集则这种情况概率为 ;否则你将等概率的获得一个元素,满足这个元素的价值不大于任何一个已经获得的元素的价值。求达到(或超过)给定的一个阈值 所需的期望步数。
设 表示当前集合为 时要达到目标的期望步数。记 为达到当前状态的上一个状态集合, 为 的后继集合所组成的集族。
于是有转移:
$$f(S)=1+pf(P)+\frac{1-p}{|\operatorname{suc}S|}\sum_{V\in\operatorname{suc}S}f(V) $$由之前的定义,显然 的前驱 是唯一的,等于 。如果我们将状态看作结点,转移关系看作边,那么整个转移过程形成了一个树形结构,前驱状态是父结点,后继状态是子结点。
这个转移显然是有后效性的,为了消去后效性,我们接下来将要证明:每个点的状态 都可以由父节点 表示成 的形式,其中 是对每个 唯一的常数。
采用归纳法:对于叶结点,结论显然成立。现在考虑一个非叶结点。为了方便书写,记 ,并将刚才转移中后面的那些状态用当前结点表示,有
$$f(S)=1+pf(P)+t\sum_{v\in\operatorname{suc}S}(k_Vf(S)+b_V) $$再记
$$\sigma_k(S)=\sum_{V\in\operatorname{suc}S}k_V,\sigma_b(S)=\sum_{V\in\operatorname{suc}S}b_V $$继续推刚才的式子:
$$\begin{aligned}&f(S)=1+pf(P)+t\sigma_k(S)f(S)+t\sigma_b(S) \\ \iff& (1-t\sigma_k(S))f(S)=pf(P)+1+t\sigma_b(S) \\ \iff& f(S)=\frac{p}{1-t\sigma_k(S)}f(P)+\frac{1+t\sigma_b(S)}{1-t\sigma_k(S)}\end{aligned} $$这样便证明了当前结点也可用父结点表示,按照归纳假设,证毕。
结论证明了,做法也便明确了。我们可以按照树形 DP 那样 DFS,每次转移时维护结点的 即可。最终答案就是 ,也就是根节点的 。每个状态的可重集只需用当前的价值和、已选元素的最小值构成的二元组来刻画。
代码短得离谱。
#include <bits/stdc++.h> using namespace std; typedef pair<double,double> pdd; int n,T,a[55]; double p; pdd dfs(int sum,int mn) { if(sum>T) return make_pair(0,0); double k=0,b=0,t=(1-p)/mn; for(int i=1;i<=mn;++i) { pdd nxt=dfs(sum+a[i],i); k+=nxt.first,b+=nxt.second; } if(!sum) t=1.0/mn; return make_pair(p/(1-t*k),(1+t*b)/(1-t*k)); } int main() { while(~scanf("%lf%d%d",&p,&T,&n)) { for(int i=1;i<=n;++i) scanf("%d",a+i); sort(a+1,a+n+1); printf("%.3f\n",dfs(0,n).second); } return 0; }
- 1
信息
- ID
- 2324
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者