1. 程式人生 > >Memory and Casinos CodeForces - 712E (概率)

Memory and Casinos CodeForces - 712E (概率)

題目連結

 

題目大意:$n$個點, 每個點$i$有成功率$p_i$, 若成功走到$i+1$, 否則走到走到$i-1$, 多組詢問, 求從$l$出發, 在$l$處不失敗, 最後在$r$處勝利的概率

 

設$L[l,r]$表示從$l$出發, 在$l$處不失敗, 最後在$r$處勝利的概率,$R[l,r]$表示從$r$出發, 在$l$處不失敗, 最後在$r$處勝利的概率

 

記$l_1=L[l,mid],r_1=L[mid+1,r],l_2=R[l,mid],r_2=R[mid+1,r].$


列舉從$mid$到$mid+1$的次數, 可以得到下式

 

$$L[l,r]=l_1l_2\sum\limits_{n=0}^\infty{(1-l_2)^nr_1^n}=\frac{l_1l_2}{1-(1-l_2)r_1}$$

$$R[l,r]=r_2+(1-r_2)r_1l_2\sum\limits_{n=0}^\infty{(1-l_2)^nr_1^n}$$

$$=r_2+(1-r_2)\frac{r_1l_2}{1-(1-l_2)r_1}$$

 

直接用線段樹維護就好了

 

#include <iostream>
#include <algorithm>
#include <math.h>
#include 
<cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) #define mid ((l+r)>>1) #define lc (o<<1) #define rc (lc|1) #define ls lc,l,mid #define rs rc,mid+1,r using namespace std; const int N = 1e5+10; int n, m; struct _ { double L, R; _ () {} _ (int x, int y) {L=R=(double)x/y;} _ (
double l,double r) :L(l),R(r) {} _ operator + (const _ &rhs) const { return _(L*rhs.L/(1-(1-rhs.L)*R),rhs.R+(1-rhs.R)*R*rhs.L/(1-(1-rhs.L)*R)); } } v[N<<2]; void build(int o, int l, int r) { if (l==r) { int x, y; scanf("%d%d", &x, &y); v[o]=_(x,y); return; } build(ls),build(rs),v[o]=v[lc]+v[rc]; } void upd(int o, int l, int r, int pos, int x, int y) { if (l==r) return v[o]=_(x,y),void(); if (mid>=pos) upd(ls,pos,x,y); else upd(rs,pos,x,y); v[o]=v[lc]+v[rc]; } _ qry(int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return v[o]; if (mid<ql) return qry(rs,ql,qr); if (mid>=qr) return qry(ls,ql,qr); return qry(ls,ql,qr)+qry(rs,ql,qr); } int main() { scanf("%d%d", &n, &m); build(1,1,n); REP(i,1,m) { int op, pos, a, b; scanf("%d", &op); if (op==1) { scanf("%d%d%d", &pos, &a, &b); upd(1,1,n,pos,a,b); } else { scanf("%d%d", &a, &b); printf("%.12lf\n", qry(1,1,n,a,b).L); } } }