洛谷P3772/LOJ2263/UOJ299/BZOJ4902[CTSC2017]游戏(概率期望+矩阵+线段树)

时间:2019-08-21
本文章向大家介绍洛谷P3772/LOJ2263/UOJ299/BZOJ4902[CTSC2017]游戏(概率期望+矩阵+线段树),主要包括洛谷P3772/LOJ2263/UOJ299/BZOJ4902[CTSC2017]游戏(概率期望+矩阵+线段树)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一个显然结论是每次的期望只和前后最近的已知结果有关。

另一个结论是:若$[x,y]$内$x$次胜负状态为$a$,$y$次为$b$的期望胜利次数为$E$,概率为$P$,则钦定$x$,$y$次分别为$a$,$b$后期望为$\frac{E}{P}$。(这个可以由贝叶斯定理想)

而固定起点的$E_0,E_1$,$P_0,P_1$可以由前一个推出,典型矩阵的套路,线段树维护即可。

但是注意期望是要相加的,也就是线段树合并期望矩阵的时候要写$matE_o=matE_{lc}\times matP_{rc}+matP_{lc}\times matE{rc}$(左区间期望*这一状态在右区间的概率=左区间对完整区间期望的贡献,右区间同理),注意矩阵没有交换律,不要乘反!

#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
const int N=200050;
char opt[5];
int n;
bool d[N];
double p[N],q[N];
set<int> S;
struct mat{
    double a[2][2];
    mat(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;}
    mat(double w,double x,double y,double z){a[1][1]=w;a[1][0]=x;a[0][1]=y;a[0][0]=z;}
    inline mat operator +(const mat &b)const{return mat(a[1][1]+b.a[1][1],a[1][0]+b.a[1][0],a[0][1]+b.a[0][1],a[0][0]+b.a[0][0]);}
    inline mat operator *(const mat &b)const{return mat(a[1][0]*b.a[0][1]+a[1][1]*b.a[1][1],a[1][0]*b.a[0][0]+a[1][1]*b.a[1][0],a[0][0]*b.a[0][1]+a[0][1]*b.a[1][1],a[0][0]*b.a[0][0]+a[0][1]*b.a[1][0]);}
};
struct node{
    mat E,P;
    node(){}
    node(mat E,mat P):E(E),P(P){}
    inline node operator +(const node &b)const{return node(E*b.P+P*b.E,P*b.P);}
}sum[N<<2];
void build(int o,int L,int R){
    if(L==R)sum[o]=node(mat(p[L],0,q[L],0),mat(p[L],1-p[L],q[L],1-q[L]));
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        build(lc,L,M);build(rc,M+1,R);
        sum[o]=sum[lc]+sum[rc];
    }
}
node ask(int o,int L,int R,int x,int y){
    if(x<=L&&y>=R)return sum[o];
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        if(x<=M)if(y>M)return ask(lc,L,M,x,y)+ask(rc,M+1,R,x,y);
        else return ask(lc,L,M,x,y);
        else return ask(rc,M+1,R,x,y);
    }
}
double query(int x,int y){
    node s=ask(1,0,n+1,x+1,y);
    return s.E.a[d[x]][d[y]]/s.P.a[d[x]][d[y]];
}
int main(){
    int m,i,x;
    double ans;
    set<int>::iterator pre,nxt;
    scanf("%d%d%s%lf",&n,&m,opt,p+1);
    for(i=2;i<=n;++i)scanf("%lf%lf",p+i,q+i);
    p[0]=q[0]=1.0;d[0]=1;  //第一次胜率钦定为pi,那就钦定0次赢了
    S.insert(0);S.insert(n+1);
    build(1,0,n+1);
    ans=query(0,n+1);
    while(m--){
        scanf("%s%d",opt,&i);
        if(opt[0]=='a'){
            scanf("%d",&x);d[i]=x;  //不要用scanf("%d",d+i),scanf会把4个bool当成一个int 改掉
            pre=nxt=S.upper_bound(i);--pre;
            ans+=query(i,*nxt)+query(*pre,i)-query(*pre,*nxt);
            S.insert(i);
        }else{
            pre=nxt=S.upper_bound(i);--pre;--pre;
            ans+=query(*pre,*nxt)-query(*pre,i)-query(i,*nxt);
            S.erase(++pre);
        }
        printf("%.6lf\n",ans);
    }
    return 0;
}
View Code

原文地址:https://www.cnblogs.com/sunshine-chen/p/11388753.html