• 神奇脑洞题解——HAOI2012高速公路(数学期望,线段树)


    传送门:COGS

    实际上,拿到这道题我是懵逼的。第一感觉是线段树维护路径费用,然后就没了。

    实际上,好好想一想,应该还是可以发现一些玄机的。

    用线段树维护公路权值是个人都会吧,不会右转幼儿园。

    但是下面期望值怎么算?

    想想教练讲的,期望就是加权平均数,那么对于区间L--R的期望实际上就是

    这是分子

    分母自然是(R-L+1)*(R-L)/2

    但是好像这个鬼东西不能用正常的线段树来求啊。

    那要想个办法,能不能给他拆开?

    考虑一下对于区间[L,R]的期望,实际上某一段路一共被算了几次?

    对于第L段路,自然是(R-L+1)次,第L+1段则是(R-L)+(R-L)//两次都是从L+1开始数到R

    第L+2段路呢?(R-L-1)*3//三次都是从L+2开始数到R,那么这个时候是不是可以发现什么?

    则有:

    但是好像还不能用线段树啊,那就继续拆

    考虑把后面的一坨式子合成一个,我们可以得到

    现在是不是可以发现什么了?

    整个表达式可以分为三部分,有一项为1的,有一项为i的,有一项为i*i的,因为还要参与区间修改,而且L和R在一次查询中可以视为常数,那么是不是可以分别维护表达式的三部分?

    假定维护sum1,sum2,sum3,sum1维护区间内所有a[i]之和,sum2维护所有a[i]*i之和,sum3维护所有a[i]*i*i之和

    现在怎么进行区间修改?

    众所周知,乘法满足分配律,(x+y)z=xz+yz,而且因为对于一段路,i固定,那么我们是否可以再加两个辅助量,sum4,sum5,分别作为区间内所有i的和,所有i*i的和?

    正确性:假定现在为区间内所有元素+w,那么sum1=sum1+区间长度*w,sum2=sum2+sum4*w=所有的(a[i]+w)*所有的i=所有的a[i]*所有的i+w*所有的i,对于sum3同理。

    那么,我们就可以愉快的维护一个含有5个元素的线段树了,最终的期望答案就是sum1*(R-L-LR+1)+sum2*(L+R)-sum3,然后除去前面算好的分母即可。

    注意,此题细节问题很多,代码中的注释几乎都是调试时输出的辅助量,用来判断程序某一部分的正确性的。

    补一句,这一题给出的区间是收费站,也就是端点,但是为了应用线段树,手动将左边界++即可(相当于每个区间的序号是自己右边的收费站)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #define int long long int
    using namespace std;
    struct PE
    {
        int lazy,sum1,sum2,sum3,sum4,sum5,l,r;
        //sum1=∑a[i]
        //sum2=∑a[i]*i
        //sum3=∑a[i]*i*i
        //sum4=∑i;
        //sum5=∑i*i;
        //4,5用于更新2,3 
    };
    PE t[400001];
    int a[100001];
    int n,m,a1,a2,a3;
    char xd;
    int ls(int x)
    {
        return x<<1;
    }
    int rs(int x)
    {
        return x<<1|1;
    }
    void pushup(int x)
    {
        t[x].sum1=t[ls(x)].sum1+t[rs(x)].sum1;
        t[x].sum2=t[ls(x)].sum2+t[rs(x)].sum2;
        t[x].sum3=t[ls(x)].sum3+t[rs(x)].sum3;
        
        t[x].l=t[ls(x)].l;
        t[x].r=t[rs(x)].r;
    }
    void build(int x,int l,int r)
    {
        if(l==r)
        {
            t[x].sum1=a[l];
            t[x].sum2=a[l]*l;
            t[x].sum3=a[l]*l*l;
            t[x].sum4=l;
            t[x].sum5=l*l;
            t[x].lazy=0;
            t[x].l=t[x].r=l;
            return ; 
        }
        int mid=(l+r)>>1;
        build(ls(x),l,mid);
        build(rs(x),mid+1,r);
        t[x].sum4=t[ls(x)].sum4+t[rs(x)].sum4;
        t[x].sum5=t[ls(x)].sum5+t[rs(x)].sum5;
        pushup(x);
    }
    void pushdown(int x,int l,int r)
    {
        int mid=(l+r)>>1;
        if(t[x].lazy)
        {
            t[ls(x)].lazy+=t[x].lazy;
            t[rs(x)].lazy+=t[x].lazy;
            t[ls(x)].sum1+=(mid-l+1)*t[x].lazy;
            t[rs(x)].sum1+=(r-mid)*t[x].lazy;
            t[ls(x)].sum2+=(t[ls(x)].sum4*t[x].lazy);
            t[ls(x)].sum3+=(t[ls(x)].sum5*t[x].lazy);
            t[rs(x)].sum2+=(t[rs(x)].sum4*t[x].lazy);
            t[rs(x)].sum3+=(t[rs(x)].sum5*t[x].lazy);
            t[x].lazy=0;
        }
    }
    void QZADD(int x,int l,int r,int nl,int nr,int w)
    {
        if(nl<=l&&nr>=r)
        {
            t[x].sum1+=(r-l+1)*w;
            t[x].sum2+=(t[x].sum4*w);
            t[x].sum3+=(t[x].sum5*w);
            t[x].lazy+=w;
            return;
        }
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if(nl<=mid)
            QZADD(ls(x),l,mid,nl,nr,w);
        if(nr>mid)
            QZADD(rs(x),mid+1,r,nl,nr,w);
        pushup(x);
    }
    int QZQUERY1(int x,int l,int r,int nl,int nr)
    {
        int kel=0;
        if(nl<=l&&nr>=r)
        {
            return t[x].sum1;
        }
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if(nl<=mid)
            kel+=QZQUERY1(ls(x),l,mid,nl,nr);
        if(nr>mid)
            kel+=QZQUERY1(rs(x),mid+1,r,nl,nr);
        return kel;
    }
    int QZQUERY2(int x,int l,int r,int nl,int nr)
    {
        int kel=0;
        if(nl<=l&&nr>=r)
        {
            return t[x].sum2;
        }
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if(nl<=mid)
            kel+=QZQUERY2(ls(x),l,mid,nl,nr);
        if(nr>mid)
            kel+=QZQUERY2(rs(x),mid+1,r,nl,nr);
        return kel;
    }
    int QZQUERY3(int x,int l,int r,int nl,int nr)
    {
        int kel=0;
        if(nl<=l&&nr>=r)
        {
            return t[x].sum3;
        }
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if(nl<=mid)
            kel+=QZQUERY3(ls(x),l,mid,nl,nr);
        if(nr>mid)
            kel+=QZQUERY3(rs(x),mid+1,r,nl,nr);
        return kel;
    }
    int QUERY(int l,int r)
    {
        int s=0;
        s+=QZQUERY1(1,1,n,l,r)*(r-l-r*l+1);
        //cout<<QZQUERY1(1,1,n,l,r)<<' ';
        s+=QZQUERY2(1,1,n,l,r)*(l+r);
        //cout<<QZQUERY2(1,1,n,l,r)<<' ';
        s-=QZQUERY3(1,1,n,l,r);
        //cout<<QZQUERY3(1,1,n,l,r)<<'*'<<endl;
        return s;
    }
    int GCD(int x,int y)
    {
        return y==0?x:GCD(y,x%y);
    }
    int LINYIN()
    {
        freopen("roadxw.in","r",stdin);
        freopen("roadxw.out","w",stdout);
        scanf("%lld%lld",&n,&m);
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            cin>>xd;
            if(xd=='C')
            {
                scanf("%lld%lld%lld",&a1,&a2,&a3);
                QZADD(1,1,n,a1+1,a2,a3);
            }
            else
            {
                scanf("%d%d",&a1,&a2);
                int kel=QUERY(a1+1,a2);
                int s=(a2-a1+1)*(a2-a1)/2;
                int G=GCD(s,kel);
                //cout<<endl<<kel<<' '<<s<<' '<<G<<endl; 
                printf("%lld/%lld
    ",kel/G,s/G);
            }
        }
        return 0;
    }
    int LWH=LINYIN();
    signed main()
    {
        ;
    }

    完结撒花!

  • 相关阅读:
    《失业的程序员》(十):分歧的产生
    《失业的程序员》(九):创业就是一场戏
    使用MySQL处理百万级以上数据时,不得不知道的几个常识
    《失业的程序员》(八):创业的要素
    《失业的程序员》(七):梦想和胸襟-正文
    《失业的程序员》(六):加班
    《失业的程序员》(五):商战之前
    《失业的程序员》(四):关于猪刚烈
    《失业的程序员》(二):酒后的第一桶金
    《失业的程序员》(三):口才帝和表情帝
  • 原文地址:https://www.cnblogs.com/XLINYIN/p/11780232.html
Copyright © 2020-2023  润新知