CF712E Memory and Casinos 期望,概率复习

题目链接

CF712E Memory and Casinos

\(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)\)*\(\displaystyle\sum^{+\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)\displaystyle\sum^{+\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;
}
上一篇:markdown基本语法


下一篇:linux网络编程之本地套接字通信