习题:Memory and Casinos(线段树&概率&推式子)

题目

传送门

思路

注意题目中最后需要达到\(r+1\)号赌场

设到\(i\)不走出$r+1 \(的概率为\)f_i$

为了简便,我们定义\(p_i=\frac{x_i}{y_i}\)

根据题意,有一个显然的方程

\(f_i=f_{i+1}p_i+f_{i-1}*(1-p_i)\)

\(f_i=f_{i+1}p_i+f_{i-1}-f_{i-1}*p_i\)

\(f_i-f_{i-1}=p_i(f_{i+1}-f_{i-1})\)

定义\(g_i=f_i-f_{i-1}\)

\(g_i=p_i(g_{i+1}+g_{i})\)

\(g_i(1-p_i)=p_ig_{i+1}\)

\(g_{i+1}=\frac{(1-p_i)}{p_i}g_i\)

根据\(f\)的定义,显然有\(f_{r+1}=1,f_{l-1}=0\)

\(\begin{aligned}1&=f_{r+1}-f_{l-1}\\&=\sum_{i=l}^{r+1}g_i\\&=g_l*\sum_{i=l}^{r+1}\prod_{j=l}^{i-1}\frac{1-p_j}{p_j}\end{aligned}\)

这里进行一点特殊说明,如果i-1<l则累加上去为的值1

然后就可以算出\(g_l\)的值

之后?

\(g_{l}=f_l-f_{l-1}=f_l\)

现在主要考虑如何后面快速后面那一团乘积

可以考虑用差分的方式

定义\(t_i=\frac{1-p_i}{p_i}\)

首先我们将\(1\)分离出来

原式即为\(1+\sum_{i=l}^{r}\prod_{j=l}^{i}t_j\)

明显有

\((\prod_{i=1}^{l-1}t_i)*\sum_{i=l}^{r}\prod_{j=l}^{i}t_j=\sum_{i=1}^{r}\prod_{j=1}^{i}t_j-\sum_{i=1}^{l-1}\prod_{j=1}^{i}t_j\)

\(\sum_{i=l}^{r}\prod_{j=l}^{i}t_j=\frac{\sum_{i=1}^{r}\prod_{j=1}^{i}t_j-\sum_{i=1}^{l-1}\prod_{j=1}^{i}t_j}{\prod_{i=1}^{l-1}t_i}\)

此时我们只需要用线段树组分别维护\(\sum_{i=1}^{x}\prod_{j=1}^{i}t_j\)\(\prod_{i=1}^{x}t_i\)

代码

#include<iostream>
using namespace std;
int n,m;
int opt;
int a,b,c;
double p[100005];
struct node
{
    int l;
    int r;
    double w1;
    double w2;
}tre[400005];
void build(int l,int r,int k)
{
    tre[k].l=l;
    tre[k].r=r;
    if(l==r)
    {
        tre[k].w1=tre[k].w2=p[l];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    tre[k].w1=tre[k<<1].w1*tre[k<<1|1].w1;
    tre[k].w2=tre[k<<1].w2+tre[k<<1].w1*tre[k<<1|1].w2;
}
void change(int u,int k,double a)
{
    if(tre[k].l>u||tre[k].r<u)
        return;
    if(tre[k].l==tre[k].r)
    {
        tre[k].w1=tre[k].w2=a;
        return;
    }
    change(u,k<<1,a);
    change(u,k<<1|1,a);
    tre[k].w1=tre[k<<1].w1*tre[k<<1|1].w1;
    tre[k].w2=tre[k<<1].w2+tre[k<<1].w1*tre[k<<1|1].w2;
}
double ask1(int l,int r,int k)
{
    if(l>tre[k].r||tre[k].l>r)
        return 1;
    if(l<=tre[k].l&&tre[k].r<=r)
        return tre[k].w1;
    double ret=1;
    ret=ret*ask1(l,r,k<<1);
    ret=ret*ask1(l,r,k<<1|1);
    return ret;
}
double ask2(int l,int r,int k)
{
    if(l>tre[k].r||tre[k].l>r)
        return 0;
    if(l<=tre[k].l&&tre[k].r<=r)
        return tre[k].w2;
    double ret=0;
    ret+=ask2(l,r,k<<1);
    if(r>((tre[k].l+tre[k].r)>>1))
		ret+=ask2(l,r,k<<1|1)*ask1(l,(tre[k].l+tre[k].r)>>1,k<<1);
    return ret;
}
void debug(int k)
{
    cout<<tre[k].w1<<' '<<tre[k].w2<<'\n';
    if(tre[k].l==tre[k].r)
    {
        cout<<"f\n";
        return;
    }
    debug(k<<1);
    debug(k<<1|1);
    

}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a,b;
        cin>>a>>b;
        p[i]=1.0*(b-a)/a;
        //cout<<p[i]<<' ';
    }
    //cout<<'\n';
    build(1,n,1);
    //debug(1);
    for(int i=1;i<=m;i++)
    {
        cin>>opt;
        if(opt==1)
        {
            cin>>a>>b>>c;
            p[a]=1.0*(c-b)/b;
            change(a,1,p[a]);
        }
        else
        {
            cin>>a>>b;
            printf("%.5lf\n",1/(ask2(a,b,1)+1));
        }
        //debug(1);
        /*for(int j=1;j<=n;j++)
            cout<<p[j]<<' ';
        cout<<'\n';*/
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/loney-s/p/13160479.html