【笔记】概率与期望
在\([0,w]\)中等概率发出一个红包,期望发出一个\(\dfrac{w}{2}\)的红包,即可以看成每次发出\(\dfrac{w}{2}\)的红包。答案就是 \(w\times 2^{-k}\) 。
期望\(E=\sum p(x)v(x)\)。
这题运用期望的线性性质,对于每个特殊格子,拆开算贡献。对于一般格子,推下式子然后矩阵乘法优化即可。
期望\(\rm DP\)入门题,\(f[i][j][0/1]\) 表示前 \(i\) 节颗,换了 \(j\) 次教室,上次有没有换教室的期望值,讨论一下转移即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 2005
#define M 305
using namespace std;
int d[N][N],n,m,t,e,u[N],v[N];double k[N];
double f[N][N][2];
int main(){
memset(d,0x3f,sizeof(d));
scanf("%d%d%d%d",&n,&m,&t,&e);
rep(i,1,n)scanf("%d",&u[i]);
rep(i,1,n)scanf("%d",&v[i]);
rep(i,1,n)scanf("%lf",&k[i]);
rep(i,1,e){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
d[x][y]=d[y][x]=min(d[x][y],z);
}
rep(r,1,t)d[r][r]=0;
rep(r,1,t)rep(i,1,t)rep(j,1,t)d[i][j]=min(d[i][j],d[i][r]+d[r][j]);
rep(i,1,n)rep(j,0,m)f[i][j][0]=f[i][j][1]=998244353.114;
f[1][0][0]=f[1][1][1]=0;
/*rep(i,1,t){
rep(j,1,t)printf("%d ",d[i][j]);
putchar('\n');
}*/
rep(i,2,n){
rep(j,1,m){
f[i][j][0]=min(
f[i-1][j][1]+k[i-1]*d[u[i]][v[i-1]]+(1.00-k[i-1])*d[u[i]][u[i-1]],
f[i-1][j][0]+d[u[i]][u[i-1]]);
f[i][j][1]=min(
f[i-1][j-1][0]+k[i]*d[v[i]][u[i-1]]+(1.00-k[i])*d[u[i]][u[i-1]],
f[i-1][j-1][1]+k[i]*k[i-1]*d[v[i]][v[i-1]]+k[i]*(1.00-k[i-1])*d[v[i]][u[i-1]]
+(1.00-k[i])*k[i-1]*d[u[i]][v[i-1]]+(1.00-k[i])*(1.00-k[i-1])*d[u[i]][u[i-1]]);
}
f[i][0][0]=f[i-1][0][0]+d[u[i-1]][u[i]];
}
double ans=998244353.00;
rep(i,0,m)ans=min(ans,min(f[n][i][0],f[n][i][1]));
printf("%.2lf\n",ans);
return 0;
}
第一问,期望的现实意义。
当标本趋于无穷大时,或理想状态时,期望可以看作实际值。
这里定义 \(f[i]\) 表示 \(i\) 个叶子的平均深度,可以看作一颗所有叶子深度都为 \(f[i]\) 的树,所以有 \(f[i]=f[i-1]+\dfrac{2}{i}\) 。
第二问,期望的线性性质。
我们可以求出概率 \(f[i][j]\) ,表示 \(i\) 个节点 ,深度\(\ge j\)的概率,枚举一下左右子树的大小和深度转移即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 105
using namespace std;
namespace task1{
double f[N];int n;
void main(){
scanf("%d",&n);
f[1]=0;
rep(i,2,n){
f[i]=f[i-1]+2.00/(i);
}
printf("%.6lf\n",f[n]);
}
}
namespace task2{
double f[N][N];int n;
void main(){
scanf("%d",&n);
rep(i,1,n)f[i][0]=1.00;
rep(i,2,n)rep(j,1,i-1){
rep(l,1,i-1)
f[i][j]+=(f[l][j-1]+f[i-l][j-1]-f[l][j-1]*f[i-l][j-1]);
f[i][j]/=i-1;
}
double ans=0;
rep(i,1,n-1)ans+=f[n][i];
printf("%.6lf\n",ans);
}
}
int main(){
int op;scanf("%d",&op);
if(op==1)task1::main();
else task2::main();
return 0;
}
期望的线性性质。
我们定义状态,求出 \(f[k][i][j]\) 表示 \(k\) 次操作后,第 \(i\) 个人剩余血量 \(j\) 的概率。
期望\(\rm DP\)。
期望\(\rm DP\)一般倒叙转移,概率\(\rm DP\)一般顺序转移。
因为对于当前状态\(f[i]\),能转移到\(f[a_1],f[a_2],\cdots\)的概率是相等的或易于计算的,顺序递推概率即可。
而计算期望的时候,我们不仅要考虑转移的概率,还需要考虑当前状态出现的概率。而我们倒叙转移可以忽略当前状态出现的概率。
这里定义\(f[i][S]\)表示前\(i-1\)个人选过的关卡集合为\(S\)的最大价值。\(f[i+1][S|2^k]\to f[i][S]\)
。
很明显的序列\(\rm DS\)题,根据期望的定义,我们求出所有可能的权值之和,除以方案数,就是期望。
这道题的关键在于攻击一次后一轮结束并将牌弃掉。
首先,\(P_{\texttt{打出}}=1-P_{\texttt{没打出}}\) 。
打出的概率不好计算,但是没有打出的概率很好计算,就是一直将牌捏在手里。
第一张牌,需要选择\(r\)次,没有打出的概率为\((1-P_i)^r\)。
第 \(i\) 张牌,如果前面一直没有打出牌,则也需要选择 \(r\) 次,否则前面打出多少牌,就要少选多少次。
那么我们可以定义 \(f[i][j]\) 表示前 \(i\) 张牌打出 \(j\) 张的概率,分打出第 \(i\) 张牌和不打出第 \(i\) 张牌讨论。有转移方程
最后对每张牌统计贡献即可。
思维题。
首先考虑最小化操作数,一个想法是从高位向低位依次熄灭即可。
事实上这就是最小代价,并且是唯一的。因为任何一个按键都是唯一的,不能通过其他组合键替换,所以我们要关掉最高位的灯,就必须按它。
既然是唯一的,那么我们就得到了必须按的按钮。那么题目也就很清晰了,每次操作,可能按到必须键,也可能按了非必须键。
定义状态 \(f[i]\) 表示还有 \(i\) 个必须键的期望次数,不难得到等式
看上去可以高斯消元,但是时间过不去。
一个巧妙的转换是利用差分,定义 \(g[i]\) 为 \(f[i]-f[i-1]\),则有方程
这就可以线性转移了。
我们考虑当前点 \(i\) ,如果它被点亮,可能是被树上任意一个点亮,不好计算。
考虑容斥,我们求出它不被点亮的概率,就是所有点都无法点亮它。
我们以当前点为根,定义 \(f[i]\) 表示以节点 \(i\) 为根的子树中,没有点亮 \(i\) 的概率。
很显然点亮不能跨越子树,因为跨越子树后,子树的根一定会被点亮,所以这样做是对的,\(\rm DP\)下去即可。
要求出以任意点为根的情况,直接二次扫描换根即可。
定义 \(f[i]\) 表示 \(i\) 个人中剩下第一个人的概率。
首先这一轮中第一个人不能死,然后枚举后面死了哪些人,可以得到等式
直接做是\(\rm O(N^2)\),还可以二项式反演的样子。
但是这道题不取模,直接组合数会爆精度,所以我们考虑换一种方法。
定义 \(f[i][j]\) 表示 \(i\) 个人中剩第 \(j\) 个人的概率。
对于\(j\ge 2\),有 \(f[i][j]=P\times f[i-1][j-1]+(1-P)\times f[i][j-1]\)。
对于\(j=1\),有\(f[i][j]=(1-P)\times f[i][i]\)。
直接消元是 \(\rm O(N^4)\) 的,但是发现每个式子只有两个未知数,直接迭代即可做到\(\rm O(N^2)\)。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 10005
using namespace std;
int n,k;
double p,f[2][N];
int main(){
//freopen("INPUT","r",stdin);
scanf("%lf%d%d",&p,&n,&k);
if(p==0){if(n==1)puts("1");else puts("0");return 0;}
f[1][1]=1;
rep(i,2,n){
int cur=i&1,pre=cur^1;
memset(f[cur],0,sizeof(f[cur]));
double u=0,v=0;
pre(j,i,2)v+=(u+1)*p*f[pre][j-1],u=(u+1)*(1-p);
f[cur][1]=(1-v)/(u+1);
rep(j,2,i)f[cur][j]=(1-p)*f[cur][j-1]+p*f[pre][j-1];
}
printf("%.10lf\n",f[n&1][k]);
return 0;
}