zoukankan      html  css  js  c++  java
  • 算法学习——吉司机线段树

    吉司机线段树

    引入:

    训练赛遇到一个题打铁的匠,需要做到询问区间大于x的和。

    赛后学习到,需要用到吉司机树的技巧。
    引入的原因十分显然:

    • 经典问题:给定一个序列,支持区间赋值 (min/max(a[i],x)) 以及区间求和。
    • 每次修改的时间复杂度为(log)(log^2)

    算法构造:

    想要实现这种操作必然离不开线段树。
    (min) 为例,对于线段树的每个节点(p)我们需要维护四个值:

    • (mx) :区间最大值
    • (cnt) :最大值出现的次数
    • (md) :次大值(严格小于mx且最大)
    • (sum) :区间和

    对于节点(p),进行如下操作

    1. (x geq mx) 直接return。
    2. (md leq x leq mx, sum-=cnt imes (mx-x))(也就是只需要对mx修改)。
    3. (x leq md),暴力递归。

    与区间加结合:

    • 只需多加一个加法标记。
    • 复杂度多一个(log)

    复杂度的分析听说需要用到势能分析,吉老师已经证明过了,具体还需要看集训队论文。

    代码实现:

    #define ls p<<1
    #define rs p<<1|1
    struct Seg
    {
        int p,l,r,mx,md,c,mi;
        ll sum;
    #define l(i) t[i].l
    #define r(i) t[i].r
    #define c(i) t[i].c
    #define mx(i) t[i].mx
    #define md(i) t[i].md
    #define sum(i) t[i].sum
    #define mi(i) t[i].mi
    }t[N<<2];
    
    void pushup(int p)
    {
        sum(p)=sum(ls)+sum(rs);
        mx(p)=max(mx(ls),mx(rs));
        md(p)=max(md(ls),md(rs));
        mi(p)=min(mi(ls),mi(rs));
        c(p)=0;
        if(mx(ls)!=mx(rs)) md(p)=max(md(p),min(mx(ls),mx(rs)));
        if(mx(p)==mx(ls)) c(p)+=c(ls);
        if(mx(p)==mx(rs)) c(p)+=c(rs);
    }
    void build(int p,int l,int r)
    {
        l(p)=l;r(p)=r;
        if(l==r)
        {
            mi(p)=mx(p)=sum(p)=a[l];
            md(p)=-1;
            c(p)=1;
            return;
        }
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(p);
    }
    void update(int p,int z)
    {
        if(z>=mx(p)) return;
        sum(p)-=(mx(p)-z)*c(p);
        mx(p)=z;
    }
    void pushdown(int p)
    {
        update(ls,mx(p));
        update(rs,mx(p));
    }
    void change(int p,int l,int r,int z)
    {
        if(z>=mx(p)) return;
        if(l<=l(p)&&r>=r(p)&&md(p)<z)
        {
            update(p,z);
            return;
        }
        int mid=l(p)+r(p)>>1;
        pushdown(p);
        if(l<=mid) change(ls,l,r,z);
        if(r>mid) change(rs,l,r,z);
        pushup(p);
    }
    

    虽然看起来长得跟一般的线段树差不多,但是它对于(min)的处理还是需要借鉴和学习。

    PS:上面的那道题只需要用到询问操作只需要做一些相应的修改(可以参考修改操作)

    代码链接

  • 相关阅读:
    红旗桌面版本最新行使方法和效果解答100例4
    红旗桌面版本最新运用行为和标题问题解答100例3
    红旗桌面版本最新运用方式和成效解答100例2
    Linux下Resin JSP MySQL的部署和设置配备铺排2
    在Linux下Turbomail简易快捷的装置体式格局
    企业级Linux红旗桌面版可以可能登岸日本
    从红旗5.0说起——看Linux的内存管理
    红旗Linux桌面4.1文本布置过程图解(一)
    Linux下Resin JSP MySQL的装置和设置装备安排1
    Ubuntu 8.04中竖立Apache PHP MySQL基本环境
  • 原文地址:https://www.cnblogs.com/Suiyue-Li/p/12754800.html
Copyright © 2011-2022 走看看