今天胃痛了一天……待在家里都不知道该做些什么……又不想学习,又不想打球……
看到自己洛谷的任务计划还有那么长,心中又浮起一丝惆怅……
题目
思路
序列问题,直接 猜想是线段树或者分块。二者的共同点:涉及 区间合并。只要有区间合并,就可以随便套。
想到这个就比较简单。全过程分解成多个小区间的方式很唯一嘛。对于每个区间,用 f l , f r f_l,f_r fl,fr 表示,从左(右)端点开始,最终从左(右)端点走出去的概率。
转移便比较简单,以左进左出为例。现在有两个区间
a
l
,
a
r
a_l,a_r
al,ar 和
b
l
,
b
r
b_l,b_r
bl,br ,那么左进左出要么经过了中线(到达过
b
b
b 部)要么没有。如果经过中线,可能会反复横跳,枚举往返次数,则有
f
l
=
a
l
+
∑
i
=
0
+
∞
(
1
−
a
l
)
⋅
b
l
⋅
(
a
r
b
l
)
i
⋅
(
1
−
a
r
)
f_l=a_l+\sum_{i=0}^{+\infty}(1-a_l)\cdot b_l\cdot(a_rb_l)^i\cdot(1-a_r)
fl=al+i=0∑+∞(1−al)⋅bl⋅(arbl)i⋅(1−ar)
无穷级数大家都会嘛。所以
f
l
=
a
l
+
(
1
−
a
l
)
(
1
−
a
r
)
b
l
1
−
a
r
b
l
f_l=a_l+{(1-a_l)(1-a_r)b_l\over 1-a_rb_l}
fl=al+1−arbl(1−al)(1−ar)bl
你突然担心起来——如果 a r b l = 1 a_rb_l=1 arbl=1 咋整?此种情形下, a a a 中必然有一个地方是必赢,否则是有从左边出去的可能性的。而题目中保证了获胜概率单调不降,所以 b b b 中应当都为必赢。进而 b l = 0 b_l=0 bl=0 。所以不会有除以零的情况。
复杂度 O ( n log n + q log n ) \mathcal O(n\log n+q\log n) O(nlogn+qlogn) 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
struct Node{
double l, r; Node(){}
Node(double L,double R):l(L),r(R){}
};
Node operator + (const Node &a,const Node &b){
return Node(
a.l+(1-a.l)*(1-a.r)*b.l/(1-a.r*b.l),
b.r+(1-b.r)*(1-b.l)*a.r/(1-a.r*b.l)
);
}
const int MaxN = 100005;
Node val[MaxN<<1]; int n;
# define id(l,r) (((l)+(r))|((l)!=(r)))
void modify(int qid,double v,int l=1,int r=n){
if(l == r){
val[l<<1].l = 1-v; // lose
val[l<<1].r = v; // win
return ;
}
int mid = (l+r)>>1;
if(mid < qid) modify(qid,v,mid+1,r);
else modify(qid,v,l,mid);
val[id(l,r)] = val[id(l,mid)]
+ val[id(mid+1,r)]; // pushUp
}
Node query(int ql,int qr,int l=1,int r=n){
if(ql <= l && r <= qr)
return val[id(l,r)];
int mid = (l+r)>>1;
if(qr <= mid) // only left
return query(ql,qr,l,mid);
if(mid < ql) // only right
return query(ql,qr,mid+1,r);
return query(ql,qr,l,mid)
+ query(ql,qr,mid+1,r);
}
int main(){
n = readint(); int q = readint();
for(int i=1,a,b; i<=n; ++i){
a = readint(), b = readint();
modify(i,1.0*a/b);
}
for(int opt,a,b,x; q; --q){
opt = readint();
if(opt == 1) x = readint();
a = readint(), b = readint();
if(opt == 1)
modify(x,1.0*a/b);
if(opt == 2)
printf("%.10f\n",1-query(a,b).l);
}
return 0;
}