CF712E Memory and Casinos
这一看就很线段树。于是我们考虑把问题转化成有结合律的信息。
下面对于一个询问 \([l,r]\) 进行讨论。
我们记 \(a_i\) 为从 \(i\to r\) 的概率。(你会发现确实存在无限循环的情况,但是我们对式子进行转化,使得可以维护)。
(默认下 \(a_0=a_l=0,a_r=a_n=n\))而有
\[a_i=a_{i-1}(1-p_i)+a_{i+1}p_i
\]
移项得
\[p_i(a_{i+1}-a_{i-1})=a_i-a_{i-1}
\]
我们定义 \(a_{1\cdot\cdot n}\) 的差分 \(b_i=a_i-a_{i-1}\)。
因为 \(a_{i+1}-a_{i-1}=b_{i+1}+b_i,a_i=a_{i-1}=b_i\)。
所以
\[p_i(b_{i+1}+b_i)=b_i
\]
移项,即
\[\dfrac{1-p_i}{p_i}\times b_i=b_{i+1}
\]
我们设 \(v_i=\dfrac{1-p_i}{p_i}\),这时观察到有
\[1+\boxed{\sum_{i=1}^n\prod_{j=1}^iv_j}=\dfrac 1{b_1}
\]
所以我们用线段树维护框出部分的值就行了。
维护
\[\begin{cases}f_{l,r}=\prod_{j=l}^rv_j\\g_{l,r}=\sum_{i=l}^r\prod_{j=l}^iv_j\end{cases}
\]
转移有(\(u,v\) 分别是左儿子和右儿子)
\[\begin{cases}f_i=f_uf_v\\g_i=g_u+f_ug_v\end{cases}
\]
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5,S = N << 2;
int n,m,cc = 0;
namespace ST{
using node = pair<double,double>; node T[S];
#define f first
#define g second
#define lc (i << 1)
#define rc (i << 1 | 1)
#define mid ((L + R) >> 1)
#define id int i,int L,int R
auto mrg(const auto &cl,const auto &cr){
return node(cl.f * cr.f,cl.g + cl.f * cr.g);
}
void build(id){
if(L == R){
int p,q; scanf("%d%d",&p,&q);
double v = 1.0 * (q - p) / p;
T[i] = node(v,v);
return;
}
build(lc,L,mid);
build(rc,mid + 1,R);
T[i] = mrg(T[lc],T[rc]);
}
void upd(int v,id){
if(L == R){
int p,q; scanf("%d%d",&p,&q);
double v = 1.0 * (q - p) / p;
T[i] = node(v,v);
return;
}
v <= mid ? upd(v,lc,L,mid) : upd(v,rc,mid + 1,R);
T[i] = mrg(T[lc],T[rc]);
}
node qry(int l,int r,id){
if(l <= L && R <= r) return T[i];
if(r <= mid) return qry(l,r,lc,L,mid);
if(l > mid) return qry(l,r,rc,mid + 1,R);
return mrg(qry(l,r,lc,L,mid),qry(l,r,rc,mid + 1,R));
}
}
int main(){
scanf("%d%d",&n,&m);
ST::build(1,1,n);
while(m--){
int op; scanf("%d",&op);
if(op == 1){
int i; scanf("%d",&i);
ST::upd(i,1,1,n);
} else {
int l,r; scanf("%d%d",&l,&r);
cc = 0;
printf("%.10f\n",1.0 / (ST::qry(l,r,1,1,n).g + 1.0));
}
}
return 0;
}

浙公网安备 33010602011771号