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:上面的那道题只需要用到询问操作只需要做一些相应的修改(可以参考修改操作)

    代码链接

  • 相关阅读:
    ubuntu 14.04搭建PHP项目基本流程
    linux下 lvm 磁盘扩容
    LVM基本介绍与常用命令
    Linux LVM逻辑卷配置过程详解
    mysql 5.7中的用户权限分配相关解读!
    linux系统维护时的一些小技巧,包括系统挂载新磁盘的方法!可收藏!
    linux系统内存爆满的解决办法!~
    源、更新源时容易出现的问题解决方法
    NV显卡Ubuntu14.04更新软件导致登录死循环,不过可以进入tty模式
    一些要注意的地方
  • 原文地址:https://www.cnblogs.com/Suiyue-Li/p/12754800.html
Copyright © 2011-2022 走看看