azelso - 期望dp

题目大意:数轴上有若干关卡,每个关卡有一个横坐标、概率(> 0);关卡分为旗帜Flag或者怪兽,概率分别表示成功立Flag或者可以打到怪兽继续向之后走的概率。没打死怪兽就会回到上一次立Flag的地方,如果没有就回到原点。每次到达一个关卡(包括重生在一个关卡)关卡会重置。也就是说怪兽这一次被你打死了下一次如果又到了这个关卡还需要再打一次;每次到达或者重生到了某个旗帜关卡,旗帜会变成倒下的,然后你仍然需要用p的概率其立起Flag。问要走多少单位距离才能到达终点。

题解:首先期望线性性,考虑每条边会被统计几次。假设这条边是从x出发的。
显然这条边被访问几次等价于你到达了几次x这一关。从第一次通关x开始记录。
如果你没有一口气在不会到x的情况下走到终点,也就是你死在之后重生在包括x之前,那么不好意思这条边你要多走一次;如果你下一次又gg了,再走一次……依次类推,显然每一次是独立的;即若你有p的概率在通关x之后,一口气冲到终点,那么你就要走期望1/p遍这条边。
考虑p怎么求,如果通关下一个关卡后一口气走到终点的概率是q,事件发生的概率是v;
如果下一个关卡是怪兽那么你要用v的概率通过这一关,然后要一口气走到底(因为不能访问x),也就是 p=vq p = v q
否则下一个位置是旗帜,考虑如果能一口气走到终点,那直接 q q ;否则如果要走两次,那必须在到达下一个关卡的时候立一个旗帜,然后q(1q)v;走三次是 q(1q)2v2 q ( 1 − q ) 2 v 2 ,依次类推,是个等比数列求和。
其实到这里快做完了,唯一的问题是等比数列求和的时候可以会有除以0的问题,解决方案是不去递推概率p,而是递推E=1/p。

#include<bits/stdc++.h>
#define gc getchar()
#define lint long long
#define mod 1000000007
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define N 100010
using namespace std;inline int inch(int x=0) { while((x=gc)!='X'&&x!='F');return x; }int p[N],c[N];lint x[N];
inline int inn() { int x,ch;while((ch=gc)<'0'||ch>'9');x=ch^'0';while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
inline lint inln() { lint x,ch;while((ch=gc)<'0'||ch>'9');x=ch^'0';while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
inline int inv(int x,int k=mod-2,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) if(k&1) ans=(lint)ans*x%mod;return ans; }
int main()
{
    lint h=inln();int n=inn(),ans=0;x[n+1]=h,p[n+1]=1,c[n+1]='X';
    rep(i,1,n) c[i]=inch(),x[i]=inln(),p[i]=inn(),p[i]=(lint)p[i]*inv(inn())%mod;
    for(int i=n,e=1;i>=0;(ans+=(x[i+1]-x[i])%mod*e%mod)%=mod,i--)
        if(c[i+1]=='X') e=(lint)e*inv(p[i+1])%mod;else e=(e+(1ll-e+mod)*p[i+1])%mod;
    return !printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值