原文链接www.cnblogs.com/zhouzhendong/p/UOJ299.html

前言

不会概率题的菜鸡博主做了一道概率题。

写完发现运行效率榜上的人都没有用心卡常数——矩阵怎么可以用数组呢?矩乘怎么可以用循环呢?

截止2019-05-15暂居运行效率榜一。

题解

首先,根据期望的线性性,容易得知,总期望等于以已知点为界的各个未知段的期望之和加上已知点的和。易知每段区间的期望只和自身转移系数和这段区间两端的已知点信息有关。

考虑到每次加入和删除信息时,只会影响 $O(1)$ 段区间的两端节点。

形式化地,我们设 $R_i$ 表示事件 “$R$ 在第 $i$ 局中胜出”, $B_i$ 表示事件 “$B$ 在第 $i$ 局中胜出”。

题意中提到的获胜概率可以表示为

$$P(R_i|R_{i-1}) = p_i,P(B_i|R_{i-1}) = 1-p_i\\P(R_i|B_{i-1}) = q_i,P(B_i|B_{i-1}) = 1-q_i$$

设行向量 $L_i = [P(R_i),P(B_i),E[R_i],E[B_i]]$,其中 $E[R_i],E[B_i]$ 到第 $i$ 局 $R$ 获胜和 $B$ 获胜时,$R$ 获胜局数的期望。

建立概率期望转移矩阵 $M_i$,使得 $L_i M_i = L_{i+1}$。容易得到:

$$M_i = \begin{bmatrix}p_i& 1-p_i& p_i & 0\\q_i& 1-q_i& q_i& 0\\0& 0& p_i&1-p_i\\0 &0 &q_i &1-q_i\end{bmatrix}$$

假设我们已经推得了某个区间的最后一个位置的概率行向量。接下来我们还要加上右侧已知信息对概率期望的影响。

我们直接求得 $L_{i+1}$,根据条件概率的计算公式,可以直接计算答案。

为了方便,我们可以设 $P(R_0) = 0, P(B_0) = 1$。

由于本题涉及 double 类型的精度问题,所以对矩阵求逆会导致过大的精度误差,所以只能使用线段树来得到区间矩阵积。

每次在修改操作的时候重算 $O(1)$ 个区间对答案的贡献即可。

时间复杂度 $O(m\log n)$ 。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof x)
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define outval(x) printf(#x" = %d\n",x)
#define outtag(x) puts("---------------"#x"---------------")
#define outarr(a,L,R) printf(#a"[%d..%d] = ",L,R);\
For(_x,L,R)printf("%d ",a[_x]);puts("")
using namespace std;
typedef long long LL;
namespace IO{
const int S=1<<20;
char I[S+1],*Is=I,*It=I,O[S+1],*Ot=O;
char gc(){return Is==It?((It=(Is=I)+fread(I,1,S,stdin))==I?EOF:*Is++):*Is++;}
void flush(){fwrite(O,1,Ot-O,stdout),Ot=O;}
void pc(char ch){Ot==O+S?flush(),*Ot++=ch:*Ot++=ch;}
struct flusher{ ~flusher(){flush();}}Flusher;
#define getchar gc
#define putchar pc
}
using IO::gc;
using IO::pc;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=200005;
struct Mat{
double v00,v01,v02,v03;
double v10,v11,v12,v13;
#define v22 v00
#define v23 v01
#define v32 v10
#define v33 v11
Mat(){}
Mat(double x){
v00=v01=v02=v03=v10=v11=v12=v13=0;
v00=v11=x;
}
Mat(double p,double q){
v00=v01=v02=v03=v10=v11=v12=v13=0;
v00=p,v01=1-p;
v10=q,v11=1-q;
v02=p,v03=0;
v12=q,v13=0;
}
friend Mat operator * (Mat A,Mat B){
Mat C(0);
C.v00=A.v00*B.v00+A.v01*B.v10;
C.v01=A.v00*B.v01+A.v01*B.v11;
C.v10=A.v10*B.v00+A.v11*B.v10;
C.v11=A.v10*B.v01+A.v11*B.v11;
C.v02=A.v00*B.v02+A.v01*B.v12+A.v02*B.v22+A.v03*B.v32;
C.v03=A.v00*B.v03+A.v01*B.v13+A.v02*B.v23+A.v03*B.v33;
C.v12=A.v10*B.v02+A.v11*B.v12+A.v12*B.v22+A.v13*B.v32;
C.v13=A.v10*B.v03+A.v11*B.v13+A.v12*B.v23+A.v13*B.v33;
return C;
}
}M[N],prod[N<<2];
int n,m;
char type[233];
double p[N],q[N],rec[N];
int s[N];// 0 -> Unknown, 1 -> R, 2 -> B
void Build(int rt,int L,int R){
if (L==R){
prod[rt]=M[L];
return;
}
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
Build(ls,L,mid);
Build(rs,mid+1,R);
prod[rt]=prod[ls]*prod[rs];
}
Mat mres;
void Query(int rt,int L,int R,int xL,int xR){
if (xL>xR||R<xL||L>xR)
return;
if (xL<=L&&R<=xR)
return (void)(mres=mres*prod[rt]);
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
Query(ls,L,mid,xL,xR);
Query(rs,mid+1,R,xL,xR);
}
set <int> S;
double getE(int L,int R){
Mat Li(0);
mres=Mat(1);
if (s[L-1]==1)
Li.v00=1;
else
Li.v01=1;
Query(1,1,n,L,R);
Li=Li*mres;
if (R==n)
return rec[L]=Li.v02+Li.v03;
Li=Li*M[R+1];
if (s[R+1]==1)
return rec[L]=Li.v02/Li.v00-1;
else
return rec[L]=Li.v03/Li.v01;
}
double readfloat(){
double x=0,w=1;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=x*10+ch-48,ch=getchar();
if (ch=='.'){
ch=getchar();
while (isdigit(ch))
w/=10,x+=w*(ch-48),ch=getchar();
}
return x;
}
void outint(int x){
if (x>9)
outint(x/10);
putchar('0'+x%10);
}
void outfloat(double x){
outint((int)x);
x-=(int)x;
putchar('.');
For(i,1,5)
x*=10,putchar('0'+(int)x),x-=(int)x;
}
void readstr(char *s){
char ch=getchar();
while (isspace(ch))
ch=getchar();
while (!isspace(ch))
*s++=ch,ch=getchar();
}
int main(){
n=read(),m=read();
readstr(type);
p[1]=readfloat(),q[1]=0;
clr(s),s[0]=1;
For(i,2,n)
p[i]=readfloat(),q[i]=readfloat();
For(i,1,n)
M[i]=Mat(p[i],q[i]);
Build(1,1,n);
S.clear(),S.insert(0),S.insert(n+1);
double now=getE(1,n);
while (m--){
readstr(type);
int x=read();
if (type[0]=='a'){
int c=read();
c=(c^1)+1;
if (c==1)
now+=1;
s[x]=c;
set <int> :: iterator it=S.lower_bound(x);
int rp=*it,lp=*--it;
S.insert(x);
now-=rec[lp+1];
now+=getE(lp+1,x-1);
now+=getE(x+1,rp-1);
}
else {
if (s[x]==1)
now-=1;
S.erase(x);
set <int> :: iterator it=S.lower_bound(x);
int rp=*it,lp=*--it;
now-=rec[lp+1];
now-=rec[x+1];
now+=getE(lp+1,rp-1);
s[x]=0;
}
outfloat(now),putchar('\n');
}
return 0;
}

  

UOJ#299. 【CTSC2017】游戏 线段树 概率期望 矩阵的更多相关文章

  1. UOJ#467. 【ZJOI2019】线段树 线段树,概率期望

    原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...

  2. LOJ#3043.【ZJOI2019】 线段树 线段树,概率期望

    原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...

  3. hdu-5805 NanoApe Loves Sequence(线段树+概率期望)

    题目链接: NanoApe Loves Sequence Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 262144/131072 ...

  4. 洛谷P2221 [HAOI2012]高速公路(线段树+概率期望)

    传送门 首先,答案等于$$ans=\sum_{i=l}^r\sum_{j=i}^r\frac{sum(i,j)}{C_{r-l+1}^2}$$ 也就是说所有情况的和除以总的情况数 因为这是一条链,我们 ...

  5. UOJ#196. 【ZJOI2016】线段树 概率期望,动态规划

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ196.html 题解 先离散化,设离散化后的值域为 $[0,m]$ . 首先把问题转化一下,变成:对于每一个位置 $i$ ...

  6. Luogu4927 梦美与线段树(线段树+概率期望)

    每个节点被经过的概率即为该区间和/总区间和.那么所需要计算的东西就是每个节点的平方和了.修改对于某个节点的影响是使其增加2sum·l·x+l2x2.那么考虑对子树的影响,其中Σl2是定值,修改后Σsu ...

  7. P3924 康娜的线段树(期望)

    P3924 康娜的线段树 看起来$O(nlogn)$可过其实由于巨大常数是无法通过的 $O(nlogn)$:70pts 我们手玩样例发现 线段树上某个节点的期望值$f[o]=(f[lc]+f[rc]) ...

  8. CodeForces - 138C: Mushroom Gnomes - 2 (线段树&概率&排序)

    One day Natalia was walking in the woods when she met a little mushroom gnome. The gnome told her th ...

  9. 洛谷P1558 色板游戏 [线段树]

    题目传送门 色板游戏 题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, .. ...

随机推荐

  1. web API .net - .net core 对比学习-使用Swagger

    根据前两篇的介绍,我们知道.net web api 和 .net core web api在配置方面的不同如下: 1. .net web api的配置是在 App_Stat文件夹里面添加对应的配置类, ...

  2. 转 C# 使用openssl

    //先用大整数来生成一个1024bit的密钥对 RSA rsa = new RSA(); BigNumber number = OpenSSL.Core.Random.Next(10, 10, 1); ...

  3. Selenium与PhantomJS踩过的坑

    Selenium与PhantomJS踩过的坑 Selenium Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动化操作, ...

  4. idea 自动生产 api文档

    第一: 打开idea,选择项目.点击工具栏 Tools->Generate JavaDOC 第二: 主要分为三部分内容. 1,Generate JavaDoc scope 要扫描生成api的范围 ...

  5. iOS自动签名网站

    node.js作为服务端,调用shell脚本进行iOS包重签名. 需要安装:nodejs ,forever 安装环境: 安装nodejs 安装forever: npm install forever ...

  6. css列表滑动防止被底部遮住和适配屏幕长一点的机型处理

    1.移动端处理列表滑动的时候,微信底下有自带的返回页面按钮,经常会被遮住,遇到屏幕长一点的,下面会短一大截,以下用此方法可以解决..container{ position:relative; back ...

  7. Redis持久化小结

    RDB RDB持久化方式是通过快照(snapshotting)完成的,当符合一定条件时,Redis将内存中所有数据以二进制方式生成一份副本并存储在硬盘上. 触发机制 save命令:阻塞当前Redis服 ...

  8. Android笔记(五十六) Android四大组件之一——ContentProvider,实现自己的ContentProvider

    有时候我们自己的程序也需要向外接提供数据,那么就需要我们自己实现ContentProvider. 自己实现ContentProvider的话需要新建一个类去继承ContentProvider,然后重写 ...

  9. System V共享内存

    目录 1. 概述 2. System V共享内存API shmget shmat shmdt shmctl 3. 简单的程序 代码实现 common.h shmcreate.c shmrmid.c s ...

  10. 资源管理与调度系统-资源管理系统Mesos

    资源管理与调度系统-资源管理系统Mesos 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Mesos是诞生于UC Berkeley的一个研究项目,它的设计动机是解决编程模型和计算框 ...