[BZOJ1426]收集邮票 题解
期望 DP
学习自:Miracle ,非常强大
Statement
种邮票,每种有无数张,收集到的概率都是 ,第 次收集代价 ,问收集所有类型邮票的期望代价
Solution
naive:粗暴地想,我只需要求出 表示已经有了 类牌还需要的期望次数,然后暴力乘一个等差数列就可以了
具体的,
然后设 表示有了 类牌然后需要的期望花费 ,其中 表示等差数列求和, ,即直接认为当前是第 次收集
但这样是不对的,因为 表示是期望次数,期望次数和 期望次数的等差数列求和并不能简单类比
也就是说, , 是一个随机变量表示有 类还需的步数,而等差数列的期望实际上是 。
注意平方的期望和期望的平方的不同,容易理解为什么不能直接等差数列算贡献
于是引入一个类似 费用提前计算 的思想,直接认为在求解 的时候是第一次收集,有这样一个式子
(你不就是把 calc 去掉了吗
也就是说,直接认为这 类都是白嫖的,然后算贡献。
但由于不是白嫖的,所以,在计算 的时候,需要把 的贡献补上。 期望买了 次,而我们之前认为这 次都是白嫖的,所以现在全部补上
(所以其实应该叫做费用推迟计算
为什么现在就是对的呢,因为现在需要算的额外贡献变成了 ”有多少个” 需要补钱,是一次方的
边界条件:,所以这样就可以做了
那我就是想要算等差数列,我就是喜欢等差数列,就真的没有办法了吗
看到上面那个式子:
就是 ,我们现在只需要算
设 表示从 到 步数平方的期望,有
哈哈,递推就好了
Code
费用推迟计算:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
double f[N],g[N],n;
signed main(){
scanf("%lf",&n);
for(int i=n-1;~i;--i)
f[i]=f[i+1]+n/(n-i),
g[i]=g[i+1]+f[i+1]+f[i]*i/(n-i)+n/(n-i);
printf("%.2f\n",g[0]);
return 0;
}
计算平方的期望:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
double f[N],h[N],n;
signed main(){
scanf("%lf",&n);
for(int i=n-1;~i;--i)
f[i]=f[i+1]+n/(n-i),
h[i]=h[i+1]+2*f[i+1]+1+(2*i*f[i]+i)/(n-i);
printf("%.2f\n",(h[0]+f[0])/2);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】