Codeforces Round #370 (Div. 2)E. Memory and Casinos[期望概率+线段树区间合并]详细推导

题目链接


题目大意:就说一个赌徒在 n n n个赌场里面转,在每个赌场他有 p i p_i pi的胜率,如果赢了就向右走,输了就向左走,如果到达 0 0 0或者 n + 1 n+1 n+1号赌场就相当退出的了赌局。定义统治区间 [ l , r ] [l,r] [l,r],就是在第 l l l个赌场不能输,并且在某次赌博中在赌场 r r r获得胜利结束赌博。
现在有两种操作:1.修改一个赌场的胜率
2.询问统治 [ l , r ] [l,r] [l,r]的概率是多少


解题思路:设 f [ i ] f[i] f[i]表示从 位 置 i 走 到 r + 1 并 且 没 有 走 出 区 间 [ l , r ] 位置i走到r+1并且没有走出区间[l,r] ir+1[l,r]的概率,那么 1. f [ i ] = f [ i + 1 ] ∗ p [ i ] + f [ i − 1 ] ∗ ( 1 − p [ i ] ) 1.f[i]=f[i+1]*p[i]+f[i-1]*(1-p[i]) 1.f[i]=f[i+1]p[i]+f[i1](1p[i])


2. 整 理 得 f [ i ] − f [ i − 1 ] = ( f [ i + 1 ] − f [ i − 1 ] ) ∗ p [ i ] 2.整理得f[i]-f[i-1]=(f[i+1]-f[i-1])*p[i] 2.f[i]f[i1]=(f[i+1]f[i1])p[i]这里 i + 1 和 i − 1 差 值 是 2 , 我 们 想 办 法 将 其 缩 小 成 1 i+1和i-1差值是2,我们想办法将其缩小成1 i+1i121


3. 设 g [ i ] = f [ i ] − f [ i − 1 ] , 那 么 上 面 得 式 子 可 以 化 简 为 g [ i ] = ( g [ i + 1 ] − g [ i ] ) ∗ p [ i ] 3.设g[i]=f[i]-f[i-1],那么上面得式子可以化简为g[i]=(g[i+1]-g[i])*p[i] 3.g[i]=f[i]f[i1],g[i]=(g[i+1]g[i])p[i]


4. 我 们 分 离 变 量 g [ i + 1 ] = 1 − p [ i ] p [ i ] ∗ g [ i ] 4.我们分离变量g[i+1]={1-p[i]\over p[i]}*g[i] 4.g[i+1]=p[i]1p[i]g[i]


5. 根 据 定 义 f [ r + 1 ] = 1 , f [ l − 1 ] = 0 ; 并 且 g [ i ] = f [ i ] − f [ i − 1 ] , 那 么 ∑ i = l r + 1 g [ i ] = f [ r + 1 ] − f [ l − 1 ] = 1 5.根据定义f[r+1]=1,f[l-1]=0;并且g[i]=f[i]-f[i-1],那么\sum_{i=l}^{r+1}g[i]=f[r+1]-f[l-1]=1 5.f[r+1]=1,f[l1]=0;g[i]=f[i]f[i1],i=lr+1g[i]=f[r+1]f[l1]=1


6. g [ l ] + g [ l + 1 ] . . . . + g [ r + 1 ] = g [ l ] + g [ l ] ∗ 1 − p [ l ] p [ l ] + . . . . , 我 们 设 u [ i ] = 1 − p [ i ] p [ i ] 那 么 后 面 g [ l ] + g [ l ] ∗ u [ l ] + g [ l ] ∗ u [ l ] ∗ u [ l + 1 ] . . . + g [ l ] ∗ u [ l ] ∗ . . . ∗ u [ r ] 全 部 变 成 g [ l ] 6.g[l]+g[l+1]....+g[r+1]=g[l]+g[l]*{1-p[l]\over p[l]}+....,我们设u[i]={1-p[i]\over p[i]}那么后面g[l]+g[l]*u[l]+g[l]*u[l]*u[l+1]...+g[l]*u[l]*...*u[r]全部变成g[l] 6.g[l]+g[l+1]....+g[r+1]=g[l]+g[l]p[l]1p[l]+....,u[i]=p[i]1p[i]g[l]+g[l]u[l]+g[l]u[l]u[l+1]...+g[l]u[l]...u[r]g[l]


7. 现 在 我 们 相 求 f [ l ] 从 上 面 可 知 f [ l ] − f [ l − 1 ] = g [ l ] , f [ l − 1 ] = 0 , 那 么 f [ l ] = g [ l ] 所 以 问 题 就 变 成 了 求 g [ l ] 7.现在我们相求f[l]从上面可知f[l]-f[l-1]=g[l],f[l-1]=0,那么f[l]=g[l]所以问题就变成了求g[l] 7.f[l]f[l]f[l1]=g[l],f[l1]=0,f[l]=g[l]g[l]


8. 那 么 我 们 把 6 式 子 中 得 g [ l ] 提 出 来 g [ l ] ∗ ( 1 + u [ l ] + u [ l ] ∗ u [ l + 1 ] + u [ l ] ∗ u [ l + 1 ] ∗ u [ l + 2 ] + . . . . . u [ l ] ∗ u [ l + 1 ] ∗ . . . . ∗ u [ r ] ) = 1 8.那么我们把6式子中得g[l]提出来g[l]*(1+u[l]+u[l]*u[l+1]+u[l]*u[l+1]*u[l+2]+.....u[l]*u[l+1]*....*u[r])=1 8.6g[l]g[l](1+u[l]+u[l]u[l+1]+u[l]u[l+1]u[l+2]+.....u[l]u[l+1]....u[r])=1


很 明 显 我 们 可 以 用 线 段 树 维 护 区 间 乘 法 , 如 过 我 们 直 接 维 护 u [ 1 , r ] 的 乘 积 然 后 再 除 以 u [ 1 , l − 1 ] 的 值 , 很 不 幸 这 道 题 卡 精 度 不 能 这 样 子 求 很明显我们可以用线段树维护区间乘法,如过我们直接维护u[1,r]的乘积然后再除以u[1,l-1]的值,很不幸这道题卡精度不能这样子求 线u[1,r]u[1,l1]


我们应该考虑如何合并区间信息:对于每个线段树的节点它维护了一个区间 [ l , r ] [l,r] [l,r],我们只看1个点的情况 v a l 1 = u [ l ] , 两 个 点 就 是 v a l 2 = u [ l ] + u [ l ] ∗ u [ l + 1 ] , 三 个 点 就 是 v a l 3 = u [ l ] + u [ l ] ∗ u [ l + 1 ] + u [ l ] ∗ u [ l + 1 ] ∗ u [ l + 2 ] , 假 设 线 段 树 左 儿 子 是 [ l , l + 1 ] , 右 儿 子 是 [ l + 2 ] , 那 么 合 并 就 是 v a l 2 + u [ l ] ∗ u [ l + 1 ] ∗ u [ l + 2 ] = v a l 3 val1=u[l],两个点就是val2=u[l]+u[l]*u[l+1],三个点就是val3=u[l]+u[l]*u[l+1]+u[l]*u[l+1]*u[l+2],假设线段树左儿子是[l,l+1],右儿子是[l+2],那么合并就是val2+u[l]*u[l+1]*u[l+2]=val3 val1=u[l],val2=u[l]+u[l]u[l+1],val3=u[l]+u[l]u[l+1]+u[l]u[l+1]u[l+2],线[l,l+1],[l+2],val2+u[l]u[l+1]u[l+2]=val3


那么我们线段树要维护两个值: v a l = u [ l ] + u [ l ] ∗ u [ l + 1 ] . . . . . , s u m = u [ l ] ∗ u [ l + 1 ] . . . . ; 合 并 就 是 v a l l e f t s o n + s u m l e f t s o n ∗ v a l r i g h t s o n = v a l f a t h e r , s u m l e f t s o n ∗ s u m r i g h t s o n = s u m f a t h e r val=u[l]+u[l]*u[l+1].....,sum=u[l]*u[l+1]....;合并就是val_{leftson}+sum_{leftson}*val_{rightson}=val_{father},sum_{leftson}*sum_{rightson}=sum_{father} val=u[l]+u[l]u[l+1].....,sum=u[l]u[l+1]....;valleftson+sumleftsonvalrightson=valfather,sumleftsonsumrightson=sumfather


下面看代码:


#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1) 
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define Setw(a) fixed<<setprecision(a)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define hash Hash
#define next Next
#define count Count
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 1e5+10, mod = 1e9 + 7;
const double eps = 1e-10;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> PII;
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
    read(first);
    read(args...);
}
struct node 
{
    double x, w;
}tr[N<<2];
int n, q;
double a[N];
inline void pushup(int rt)
{
   tr[rt].w = tr[rt << 1].w * tr[rt << 1|1].w;
   tr[rt].x = tr[rt << 1].x + tr[rt << 1].w * tr[rt << 1|1].x;
   return;
}

inline void build(int rt, int l, int r)
{
    if(l == r)
    {
        tr[rt].x = a[l];
        tr[rt].w = a[l];
        return;
    }
    build(Lson);build(Rson);
    pushup(rt);
}


inline void update(int rt, int l, int r ,int pos, double val)
{
    if(l == r)
    {
        tr[rt].w = val;
        tr[rt].x = val;
        return;
    }
    if(pos <= mid) update(Lson,pos,val);
    else update(Rson,pos,val);
    pushup(rt);
}

inline node query(int rt, int l, int r, int posl, int posr)
{
    if(posl <= l && posr >= r) return tr[rt];
    else 
    {
        node left = (node){-INF,-INF};
        node right = (node){-INF,-INF};
        if(posl <= mid) left = query(Lson,posl,posr);
        if(posr > mid) right = query(Rson,posl,posr);
        if(left.w == -INF) return right;
        else if(right.w == -INF) return left;
        else 
        {
            node res;
            res.w = left.w * right.w;
            res.x = left.x + left.w * right.x;
            return res;
        }
    }
}

int main()
{
    read(n,q);
    _rep(i,1,n) 
    {
       double l, r;
       cin >> l >> r;
       a[i] = 1.0 * l / r;
       a[i] = (1.0 - a[i]) / a[i];
    }
    build(1,1,n);
    while(q--)
    {
        int op, pos;
        double l, r;
        read(op);
        if(op == 1)
        {
            cin >> pos >> l >> r;
            double ans = (1.0 - l/r) / (l/r);
            update(1,1,n,pos,ans);
        }
        else 
        {
            cin >> l >> r;
            double ans = query(1,1,n,l,r).x;
            cout << Setw(10) << 1.0 / (ans + 1.0) << endl;
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值