题目大意
有\(n\)个赌场,在赌场\(i\)有\(p_i\)的几率获胜并走到第\(i+1\)个赌场,有\(1-p_i\)的几率失败并走到第\(i-1\)个赌场。有两种操作:
- 修改\(p_i\)
- 给定区间\([l,r]\),求从\(l\)开始并在\(r\)获胜的概率。
思路
考虑每一个查询操作,对于区间\([l,r]\),\(l\)到达不了\(r\)当且仅当经过足够多的移动之后移动到了第\(l-1\)个赌场,否则一定会移动到\(r\)。那么就能很容易地设计出一个\(O(qn)\)的算法。
我们再考虑两个区间\([l,mid],[mid+1,r]\),我们尝试合并这两个区间,使得最终可以\(O(log_2n)\)的修改和查询。那么我们需要知道哪些信息呢?因为需要在两个区间之间移动,所以我们维护:
- 区间\([l,r]\)上从\(l\)开始并在\(r\)获胜的概率,用\(p_{l,r}\)表示。
- 区间\([l,r]\)上从\(r\)开始并移动到右边区间的概率,用\(q_{l,r}\)表示。
那么有:
\[ p_{l,r}=\frac{p_{l,mid}p_{mid+1,r}}{1+q_{l,mid}(1-p_{mid+1,r})} \]
\[ q_{l,r}=q_{mid+1,r}+\frac{q_{l,mid}p_{mid+1,r}(1-q_{mid+1,r})}{1-q_{l,mid}(1-p_{mid+1,r})} \]
这个过程可以用线段树维护
提示
等比数列公式:
\[ \sum_{i=0}^{\infty}{p^i}=\frac{1}{1-p} \]
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long double extended;
const int MAXN = 100000 + 10;
struct SegmentTree {
struct Node {
extended l, r;
Node (extended l=0, extended r=0): l(l), r(r) {}
Node operator+(const Node& a)
{
return Node(
l * a.l / (1 - r * (1 - a.l)),
a.r + (r * a.l * (1 - a.r)) / (1 - r * (1 - a.l))
);
}
} p[MAXN << 2];
void pushup(int o) { p[o] = p[o<<1] + p[o<<1|1]; }
int L[MAXN << 2], R[MAXN << 2];
void build(int l, int r, int* x, int* y, int o=1)
{
L[o] = l, R[o] = r;
if (l == r) {
p[o] = Node(
(extended)x[l] / (extended)y[l],
(extended)x[l] / (extended)y[l]
);
return;
}
int mid = (l + r) >> 1;
build(l, mid, x, y, o<<1);
build(mid+1, r, x, y, o<<1|1);
pushup(o);
}
void update(int pos, int x, int y, int o=1)
{
if (L[o] == pos && pos == R[o]) {
p[o] = Node(
(extended)x / (extended)y,
(extended)x / (extended)y
);
return;
}
int mid = (L[o] + R[o]) >> 1;
if (pos <= mid)
update(pos, x, y, o<<1);
else
update(pos, x, y, o<<1|1);
pushup(o);
}
Node query(int l, int r, int o=1)
{
if (L[o] == l && r == R[o])
return p[o];
int mid = (L[o] + R[o]) >> 1;
if (r <= mid)
return query(l, r, o<<1);
if (mid < l)
return query(l, r, o<<1|1);
return query(l, mid, o<<1) + query(mid+1, r, o<<1|1);
}
extended solve(int l, int r) { return query(l, r).l; }
} segt;
int main()
{
ios::sync_with_stdio(0);
static int X[MAXN], Y[MAXN];
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> X[i] >> Y[i];
}
segt.build(1, n, X, Y);
while (m--) {
int opt; cin >> opt;
if (opt == 1) {
int i, x, y;
cin >> i >> x >> y;
segt.update(i, x, y);
} else {
int l, r;
cin >> l >> r;
cout << segt.solve(l, r) << endl;
}
}
}