• CF712E [Memort and Casinos]


    题意

    每次询问一段区间[l,r],求从最左边走到最右边(r+1)的概率(若走到l-1,则GG了),每个点上写有向右走的概率。支持单点修改。


    思考

    若只查询一次,那只要知道每个点在不走到l-1的情况下,向右移动一格的概率就行了,最后乘起来就是答案。

    但我们忽略了一件事情,若从一个区间的某一点出发,从左边走出去和右边走出去的概率和为1(不可能停在中间),于是我们要设计一个状态,并能够合并,这样才有可能优化复杂度。

    设 [l,r] 区间的L为从第l个点(最左边)出发,在右边走出去的概率,R为第R个点出发,在左边走出去的概率。

    [L1-->R1][L2-->R2]
    [ L -- -- -- --> R ]

    请记住,他们每个的实际意义。因为每个点要么从左边走出去,要么从右边走出去,所(1-L1)的实际意义就是从(左边出发,不从右边走出),即(从左边出发,从左边走出去的概率)。

    那么若有两个连续的区间,合并成一个新区间会有怎样的答案? 设左右两个区间的答案分别为L1,R1,L2,R2,则:

    L   =       L1*L2                                                                      走到右边还要再走到更右边

             +   L1(1-L2)(1-R1)*L2                                               第一次向右边走失败了,再退回来再向右走

             +   L1(1-L2)(1-R1)(1-L2)(1-R1)*L2                         反反复复.......

    +.......

    整理一下,
                       L   =   L1L2+L1L2(1-L2)(1-R1)+L1L2(1-L2)2+.......
    (1-L2)(1-R1)L   =   L1L2(1-L2)(1-R1)+L1L2(1-L2)2+.......

     

    作差,

    L(1-(1-L2)(1-R1))=L1L2

    L=L1L2/(1-(1-L2)(1-R1))

    R也可以类似地得到,但注意它们的和不一定为1,因为是两个不同的端点。

    简单地线段树维护。


    代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1E5+5;
     4 int n,m;
     5 double x,y,a[maxn];
     6 struct tree{int l,r;double L,R;}t[maxn*4];
     7 tree merge(tree x,tree y)
     8 {
     9     tree z;
    10     z.l=x.l;z.r=y.r;
    11     z.L=x.L*y.L/(1-(1-y.L)*(1-x.R));
    12     z.R=x.R*y.R/(1-(1-y.L)*(1-x.R));//不要认为反一反就对了QAQ 
    13     return z;
    14 }
    15 void build(int l,int r,int num)
    16 {
    17     t[num].l=l,t[num].r=r;
    18     if(l==r)
    19     {
    20         t[num].L=a[l];
    21         t[num].R=1-a[l];
    22         return;
    23     }
    24     int mid=(l+r)>>1;
    25     build(l,mid,num*2);build(mid+1,r,num*2+1);
    26     t[num]=merge(t[num*2],t[num*2+1]);
    27 }
    28 void change(int pos,double val,int num)
    29 {
    30     if(t[num].l==t[num].r)
    31     {
    32         t[num].L=val;
    33         t[num].R=1-val;
    34         return;
    35     }
    36     int mid=(t[num].l+t[num].r)>>1;
    37     if(pos<=mid)change(pos,val,num*2);
    38     else change(pos,val,num*2+1);
    39     t[num]=merge(t[num*2],t[num*2+1]);
    40 }
    41 tree ask(int L,int R,int num)
    42 {
    43     if(L<=t[num].l&&t[num].r<=R)return t[num];
    44     int mid=(t[num].l+t[num].r)>>1;
    45     if(R<=mid)return ask(L,R,num*2);
    46     else if(mid<L)return ask(L,R,num*2+1);
    47     else return merge(ask(L,R,num*2),ask(L,R,num*2+1));
    48 }
    49 int main()
    50 {
    51     ios::sync_with_stdio(false);
    52     cin>>n>>m;
    53     for(int i=1;i<=n;++i)
    54     {
    55         cin>>x>>y;
    56         a[i]=x/y;
    57     }
    58     build(1,n,1);
    59     while(m--)
    60     {
    61         int opt,d;
    62         cin>>opt;
    63         if(opt==1)
    64         {
    65             cin>>d>>x>>y;
    66             change(d,x/y,1);
    67         }
    68         else
    69         {
    70             int l,r;
    71             cin>>l>>r;
    72             cout<<fixed<<setprecision(8)<<ask(l,r,1).L<<endl;
    73         }
    74     }
    75     return 0;
    76 }
    View Code
  • 相关阅读:
    晨考总结第二天
    晨考总结第一天
    常用的设计模式总结
    AOP底层原理剖析
    Spring AOP
    jstat统计输出说明
    zabbix 触发器匹配字符串告警
    hive部分常用函数
    nginx日志说明
    windwos文件句柄数限制
  • 原文地址:https://www.cnblogs.com/GreenDuck/p/10543922.html
Copyright © 2020-2023  润新知