题目大意:
(图片翻译来源于:洛谷)
解题思路:
最后的最后还是看了CF的官方题解…
- L [ l , r ] L[l, r] L[l,r]代表从 l l l离开区间的概率
- R [ l , r ] R[l,r] R[l,r]代表从 r r r离开区间的概率
- 设 l 1 = L [ l , i ] , l 2 = L [ i + 1 , r ] , l e f = L [ l , r ] l1=L[l,i],l2=L[i+1,r],lef=L[l,r] l1=L[l,i],l2=L[i+1,r],lef=L[l,r]
- 设 r 1 = R [ l , i ] , r 2 = [ i + 1 , r ] , r i g = R [ l , r ] r1=R[l,i],r2=[i+1,r],rig=R[l,r] r1=R[l,i],r2=[i+1,r],rig=R[l,r]
- 线段树保存+单点修改+询问(询问与正常线段树有点差别,因为合并规则不同)即可
AC代码:
// 712E — Memory and Casinos(期望).cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <iomanip>
#include <cstdio>
//#include <bits/stdc++.h>
using namespace std;
const int maxn = 100100;
pair <double, double> seg[maxn << 2];
double p[maxn];
int n, q;
bool Judge(double x) {
return fabs(x) <= 0.0000000001;
}
pair <double, double> merge(pair <double, double> a, pair <double, double> b) {
double l1 = a.first, r1 = a.second;
double l2 = b.first, r2 = b.second;
if (Judge(l1 + 1) && Judge(r1 + 1)) return b;//这里返回是因为,该段区间并不在所求区间内
if (Judge(l2 + 1) && Judge(r2 + 1)) return a;
if (Judge(((l2 - 1) * r1 + 1))) return make_pair(0, 0);//判断分母是否为0
double lef = l1 * l2 / ((l2 - 1) * r1 + 1);
double rig = r2 + (r1 * l2 * (1 - r2)) / (1 - r1 * (1 - l2));
return make_pair(lef, rig);
}
void build(int lef, int rig, int rt) {
if (lef == rig) {
seg[rt] = make_pair(p[lef], p[lef]);//初始都为p[lef]
return;
}
int mid = (lef + rig) >> 1;
build(lef, mid, rt << 1);
build(mid + 1, rig, rt << 1 | 1);
seg[rt] = merge(seg[rt << 1], seg[rt << 1 | 1]);
}
void update(int lef, int rig, int x, double val, int rt) {
if (x < lef || x > rig) return;
if (lef == rig) {
seg[rt] = make_pair(val, val);
return;
}
int mid = (lef + rig) >> 1;
update(lef, mid, x, val, rt << 1);
update(mid + 1, rig, x, val, rt << 1 | 1);
seg[rt] = merge(seg[rt << 1], seg[rt << 1 | 1]);
}
pair <double, double> query(int L, int R, int lef, int rig, int rt) {
if (R < lef || L > rig) return make_pair(-1, -1);//不在该段区间
if (L <= lef && rig <= R) {
return seg[rt];
}
int mid = (lef + rig) >> 1;
return merge(query(L, R, lef, mid, rt << 1), query(L, R, mid + 1, rig, rt << 1 | 1));
//因为找到的子区间要重新合并,合并规则并不是简单相加,所以这里需要调用函数
}
int main()
{
int x, y;
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> x >> y;
p[i] = (double)x / (double)y;
}
build(1, n, 1);
int a, b, c, d;
for (int i = 1; i <= q; i++) {
cin >> a >> b >> c;
//scanf_s("%d%d%d", &a, &b, &c);
if (a == 1) {
cin >> d;
//scanf_s("%d", &d);
update(1, n, b, (double)c / (double)d, 1);
}
else {
printf("%.20f\n", query(b, c, 1, n, 1).first);
}
}
}