Link
设\(f(l,r)\)为从\(l\)走到\(r+1\)并且在\(l,r\)没有输过的概率,\(g(l,r)\)为从\(r\)走到\(l-1\)并且在\(l,r\)没有赢过的概率。
那么这题看上去就很线段树了对吧。
首先很显然\(f(i,i)=p_i,g(i,i)=1-p_i\)。
然后考虑合并\([l,mid],(mid,r]\)的答案。
枚举跨过\((mid,mid+1)\)这一分界线然后又走回来的次数,可以得到\(f(l,r)=f(l,mid)f(mid+1,r)\sum\limits_i((1-f(mid+1,r))(1-g(l,mid)))^i\)。
显然这个幂级数收敛,所以\(f(l,r)=\frac{f(l,mid)f(mid+1,r)}{1-(1-f(mid+1,r))(1-g(l,mid))}\)。
同理可得\(g(l,r)=\frac{g(l,mid)g(mid+1,r)}{1-(1-f(mid+1,r))(1-g(l,mid))}\)。
然后线段树随便维护一下就完事了。
#include<cstdio>
#include<cctype>
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}using IO::read;
using db=double;
const int N=100007;
struct node{db f,g;}t[N<<2];db p[N];
node operator+(const node&a,const node&b){return {a.f*b.f/(1-(1-b.f)*(1-a.g)),a.g*b.g/(1-(1-b.f)*(1-a.g))};}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
void pushup(int p){t[p]=t[ls]+t[rs];}
void modify(int p,db v){t[p]={v,1-v};}
void build(int p,int l,int r)
{
if(l==r) return modify(p,1.0*read()/read());
build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void update(int p,int l,int r,int x)
{
if(l==r) return modify(p,1.0*read()/read());
(x<=mid? update(ls,l,mid,x):update(rs,mid+1,r,x)),pushup(p);
}
node query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return t[p];
if(r<L||R<l) return {1,1};
return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
}
int main()
{
int n=read(),q=read();
build(1,1,n);
for(int x;q;--q) read()==1? update(1,1,n,read()):(x=read(),printf("%.6lf\n",query(1,1,n,x,read()).f),void());
}