题目链接
(Firstly),前言
这道题是道假的黑题
一群学弟比我先A ,我真是太弱了
我们的思路都应该是来自于这位大佬的 czpcf
自我认为他们的公式都推得很草率
所以我才决定再 写一篇
(Secondly),思路
定义 (f(i,j)) 为从赌场(i)出发 在赌场(i)赢 在赌场(j)赢并结束的概率
定义 (f(j + 1,k)) 为从赌场(j) + 1出发 在赌场(j) + 1赢 在赌场(k)赢并结束的概率
定义 (g(i,j)) 与 (f)恰相反
那么 (f(i,k)) = 直接走过去的概率(这里中间也可能转了圈 但没有过(j)) + 在中间转了圈且走过去的概率(中间的圈过了(j))
直接走过去的概率 = (f(i,j) *f(j + 1,k))
在中间转了圈且走过去的概率 = (f(i,j) *f(j + 1,k))*(displaystylesum^{+infty}_{p=1}w^p)
其中(w)为在中间转一圈的概率
重点就是 (w)怎么算
(w) = ((1-f(j+1,k))(1-g(i,j)))
这个式子是怎么得出的呢?
先理解(1-f(j+1,k))
为从赌场(j) + 1出发 在赌场(j) + 1赢 在赌场(k)赢并结束的概率
这里 (1-f(j+1,k))
赌场(j) + 1出发是默认的
条件只有
-
1:在赌场(j) + 1赢
-
2:在赌场(k)赢
两个中至少有一个不成立
但最后(1,2)造成的结果都是从右区间返回 (不管她在([j + 1,k])转多少圈)
(Additionally),化简
[f(i,k)
]
[= f(i,j) *f(j + 1,k)displaystylesum^{+infty}_{p=1}w^p
]
[= f(i,j) *f(j + 1,k)frac{1-w^{+infty}}{1-w}
]
[w^{+infty} = 0
]
[Rightarrow= frac{f(i,j) *f(j + 1,k)}{1-(1-f(j+1,k))(1-g(i,j))}
]
这下就可以用线段树维护了
(Eventually),(Code)
#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= (x)&&(x) <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
#define fi first
#define se second
typedef double db;
const int MAXN = 100010;
db p[MAXN];
struct node
{
double g,f;
}tree[MAXN << 2];
inline void sum(db &f,db &g,db f1,db f2,db g1,db g2)
{
f = f1 * f2 / (1 - (1 - f2) * (1 - g1));
g = g1 * g2 / (1 - (1 - f2) * (1 - g1));
}
inline void BuildTree(int k,int l,int r)
{
if(l == r)
{
tree[k].f = p[l],tree[k].g = 1 - p[l];
return;
}
int mid = l + r >> 1;
BuildTree(k << 1,l,mid);
BuildTree(k << 1 | 1,mid + 1,r);
sum(tree[k].f,tree[k].g,tree[k << 1].f,tree[k << 1 | 1].f,tree[k << 1].g,tree[k << 1 | 1].g);
}
inline pair<db,db> query(int k,int l,int r,int L,int R)
{
if(L <= l&&r <= R) return make_pair(tree[k].f,tree[k].g);
int mid = l + r >> 1;
pair<db,db> k1,k2;k1.fi = k2.fi = k1.se = k2.se = 1;
if(L <= mid) k1 = query(k << 1,l,mid,L,R);
if(mid < R) k2 = query(k << 1 | 1,mid + 1,r,L,R);
db T1,T2;
sum(T1,T2,k1.fi,k2.fi,k1.se,k2.se);
return make_pair(T1,T2);
}
inline void update(int k,int l,int r,int pos,db x)
{
if(l == r)
{
tree[k].f = x,tree[k].g = 1 - x;
return;
}
int mid = l + r >> 1;
if(pos <= mid) update(k << 1,l,mid,pos,x);
else update(k << 1 | 1,mid + 1,r,pos,x);
sum(tree[k].f,tree[k].g,tree[k << 1].f,tree[k << 1 | 1].f,tree[k << 1].g,tree[k << 1 | 1].g);
}
int main()
{
int n = Read(1),q = Read(1);
for(reg i = 1;i <= n;i++)
{
int a = Read(1),b = Read(1);
p[i] = (db)a / b;
}
BuildTree(1,1,n);
while(q--)
{
int sit = Read(1);
if(sit & 1)
{
int pos = Read(1),a = Read(1),b = Read(1);
update(1,1,n,pos,((db)a / b));
} else {
int l = Read(1),r = Read(1);
cout << query(1,1,n,l,r).fi << endl;
}
}
return 0;
}