原文链接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. HDFS读流程

    客户端先与NameNode通信,获取block位置信息,之后线性地先取第一个块,然后接二连三地获取,取回一个块时会进行MD5验证,验证通过后会使read顺利进行完,当最终读完所有的block块之后,拼 ...

  2. web项目中添加定时任务

    1.在web.xml中添加servlet <servlet> <servlet-name>StatisticInitServlet</servlet-name> & ...

  3. RestTemplate的GET与POST

    发送GET请求: //设置请求头HttpHeaders headers = new HttpHeaders(); headers.add("token",PostUtils.get ...

  4. 学习python的日常3

    python的一些高级特性: 切片(跟名字一样,把一个完整的东西选取一部分自己想要的去切下来):通过切片可以快速的去除一些元素,只要确定好索引位置,避免的循环导致的多写代码 数组,元组,字符串都可以用 ...

  5. Linux命令——rsync

    参考:Rsync (Remote Sync): 10 Practical Examples of Rsync Command in Linux How to Sync Files/Directorie ...

  6. Java 十大排序算法

    目录: 1.冒泡排序(Bubble Sort) 2.选择排序(Selection Sort) 3.插入排序(Insertion Sort) 4.希尔排序(Shell Sort) 5.归并排序(Merg ...

  7. Beta版本冲刺

    一.团队成员 团队名称 西柚排课王 项目名称 易奇排排课系统 团队成员 秦傲明 201731062308 韩浩 201731062319 黄青松 201731062322 王越豪 2017310623 ...

  8. docker版本lnmp

    也不是全部的docker,比如php-fpm,这个可以用docker版. 但第三方插件就不灵活,所以原生的就好. 另外,在建设ftp服务时,以后要弃vsftpd而选用pure-ftp了. pure-f ...

  9. 依赖注入(DI)和控制反转(IOC)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  10. POJ 3233 Matrix Power Series——快速幂&&等比&&分治

    题目 给定一个 $n \times n$  的矩阵 $A$ 和正整数 $k$ 和 $m$.求矩阵 $A$ 的幂的和. $$S = A + A^2 + ... + A^k$$ 输出 $S$ 的各个元素对 ...