CF712E Memory and Casinos 题解
假设只保留数组上 \([l,r]\) 的这段数,记 \(f_i\) 表示从点 \(i\) 出发到达 \(n+1\) 的概率,则有 \(f_0=0,f_{n+1}=1,f_i=(1-p_i)f_{i-1}+p_if_{i+1}\),题目要求的是 \(f_1\)。
考虑对最后一个等式进行一些变换,把 \(f_i\) 的系数拆开得:
\[p_if_i+(1-p_i)f_i=(1-p_i)f_{i-1}+p_if_{i+1}
\]
移项得:
\[(1-p_i)(f_i-f_{i-1})=p_i(f_{i+1}-f_i)
\]
记 \(d_i=f_{i+1}-f_i\),那么由上式得 \(d_i=\frac{1-p_i}{p_i}d_{i-1}\)。
显然有 \(\sum_{i=0}^nd_i=1,f_1=d_0\),于是 \(f_1=\frac 1{1+\sum_{i=1}^n d_i}\),线段树维护即可。
时间复杂度 \(\mathcal O(n\log n)\)。
参考代码:
#include<bits/stdc++.h>
#define mxn 100003
#define ld long double
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
struct node{
ld s,d;
}t[mxn<<2];
int n,q,a,b;
ld d[mxn];
inline node operator+(node x,node y){
return {x.s+x.d*(y.s-1),x.d*y.d};
}
void build(int p,int l,int r){
if(l==r){
t[p]={d[l]+1,d[l]};
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
t[p]=t[p<<1]+t[p<<1|1];
}
void change(int p,int x,int l,int r){
if(l==r){
t[p]={d[l]+1,d[l]};
return;
}
int mid=(l+r)>>1;
if(x<=mid)change(p<<1,x,l,mid);
else change(p<<1|1,x,mid+1,r);
t[p]=t[p<<1]+t[p<<1|1];
}
node ask(int p,int l,int r,int L,int R){
if(l<=L&&R<=r)return t[p];
int mid=(L+R)>>1;
if(l<=mid&&r>mid)return ask(p<<1,l,r,L,mid)+ask(p<<1|1,l,r,mid+1,R);
if(l<=mid)return ask(p<<1,l,r,L,mid);
return ask(p<<1|1,l,r,mid+1,R);
}
signed main(){
scanf("%d%d",&n,&q);
rep(i,1,n)scanf("%d%d",&a,&b),d[i]=(ld)a/b,d[i]=(1-d[i])/d[i];
build(1,1,n);
int op,x,l,r;
while(q--){
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&x,&l,&r);
d[x]=(ld)l/r;
d[x]=(1-d[x])/d[x];
change(1,x,1,n);
}else{
scanf("%d%d",&l,&r);
printf("%.9Lf\n",1/ask(1,l,r,1,n).s);
}
}
return 0;
}