假設只保留陣列上 \([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;
}