• 模板


    维护区间的线段树

    线段树主要就是在在PushUp和Query的时候注意怎么合并左右区间的信息就可以了。对于延迟标记的互相影响完全就是自己跟自己过不去,假如有多种延迟标记的话不妨在访问到一个区间时全部下推(只需要注意叶子层是不能下推的),从其他部分把常数补回来就可以了。

    例1 维护加法和(修改:单点加值 询问:区间加法和)

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 100000;
        ll a[MAXN + 5];
        ll st[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            st[o] = st[ls] + st[rs];
        }
    
        void Build(int o, int l, int r) {
            if(l == r)
                st[o] = a[l];
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
        }
    
        void Update(int o, int l, int r, int p, ll v) {
            if(l == r) {
                st[o] += v;
                return;
            } else {
                int m = l + r >> 1;
                if(p <= m)
                    Update(ls, l, m, p, v);
                if(p >= m + 1)
                    Update(rs, m + 1, r, p, v);
                PushUp(o);
            }
        }
    
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return st[o];
            } else {
                int m = l + r >> 1;
                ll res = 0;
                if(ql <= m)
                    res = res + Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = res + Query(rs, m + 1, r, ql, qr);
                return res;
            }
        }
    #undef ls
    #undef rs
    };
    

    例2 维护加法和(修改:区间加值 询问:区间加法和)

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 100000;
        ll a[MAXN + 5];
        ll st[(MAXN << 2) + 5], lazy[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            st[o] = st[ls] + st[rs];
        }
    
        void PushDown(int o, int l, int r) {
            if(lazy[o]) {
                lazy[ls] += lazy[o];
                lazy[rs] += lazy[o];
                int m = l + r >> 1;
                st[ls] += lazy[o] * (m - l + 1);
                st[rs] += lazy[o] * (r - m);
                lazy[o] = 0;
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r)
                st[o] = a[l];
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
            lazy[o] = 0;
        }
    
        void Update(int o, int l, int r, int ql, int qr, ll v) {
            if(ql <= l && r <= qr) {
                lazy[o] += v;
                st[o] += v * (r - l + 1);
                return;
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                if(ql <= m)
                    Update(ls, l, m, ql, qr, v);
                if(qr >= m + 1)
                    Update(rs, m + 1, r, ql, qr, v);
                PushUp(o);
            }
        }
    
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return st[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                ll res = 0;
                if(ql <= m)
                    res = res + Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = res + Query(rs, m + 1, r, ql, qr);
                return res;
            }
        }
    #undef ls
    #undef rs
    };
    

    例3 维护最大最小值(修改:区间加值 询问:区间最大最小值)

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 1000000;
        static const int INF = 0x3f3f3f3f;
        int mi[(MAXN << 2) + 5];
        int ma[(MAXN << 2) + 5];
        int lz[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            mi[o] = min(mi[ls], mi[rs]);
            ma[o] = max(ma[ls], ma[rs]);
        }
    
        void PushDown(int o, int l, int r) {
            if(lz[o]) {
                lz[ls] += lz[o];
                lz[rs] += lz[o];
                //int m = l + r >> 1;
                mi[ls] += lz[o];
                mi[rs] += lz[o];
                ma[ls] += lz[o];
                ma[rs] += lz[o];
                lz[o] = 0;
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r) {
                mi[o] = 0;
                ma[o] = 0;
            } else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
            lz[o] = 0;
        }
    
        void Update(int o, int l, int r, int ql, int qr, int v) {
            if(ql <= l && r <= qr) {
                lz[o] += v;
                mi[o] += v;
                ma[o] += v;
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                if(ql <= m)
                    Update(ls, l, m, ql, qr, v);
                if(qr >= m + 1)
                    Update(rs, m + 1, r, ql, qr, v);
                PushUp(o);
            }
        }
    
        int QueryMin(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return mi[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                int res = INF;
                if(ql <= m)
                    res = QueryMin(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = min(res, QueryMin(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    
        int QueryMax(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return ma[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                int res = -INF;
                if(ql <= m)
                    res = QueryMax(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = max(res, QueryMax(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    

    这种线段树可以简单拓展:加多一个标记,记录这个区间的最值出自哪个元素。尤其容易维护出最左侧/最右侧的最值。


    维护值域的线段树(权值线段树)

    需要先离线所有可能的取值,然后离散化到线段树可以接受的空间范围。每个节点存当前的值域的信息,最简单的应用是维护当前值域的元素共有多少个。那么可以在线段树上二分,返回当前线段树中的第k大。这种情形用于在某些情况下替代平衡树的功能,优点是常数相对平衡树而言很小,对于插入数据的顺序有要求,有时可能还要求离线。真正在线维护全树第k大的只有平衡树。

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 100000;
        int cnt[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            cnt[o] = cnt[ls] + cnt[rs];
        }
    
        void Build(int o, int l, int r) {
            if(l == r)
                cnt[o] = 0;
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
        }
    
        //修改值为p的元素的个数,增量为v,且不能为负
        void Update(int o, int l, int r, int p, int v) {
            if(l == r) {
                cnt[o] += v;
                if(cnt[o] < 0)
                    cnt[o] = 0;
                return;
            } else {
                int m = l + r >> 1;
                if(p <= m)
                    Update(ls, l, m, p, v);
                if(p >= m + 1)
                    Update(rs, m + 1, r, p, v);
                PushUp(o);
            }
        }
    
        //查询<=x的元素的个数
        int GetRank(int o, int l, int r, int x) {
            if(r <= x) {
                return cnt[o];
            } else {
                int m = l + r >> 1;
                if(x <= m)
                    return GetRank(ls, l, m, x);
                else
                    return cnt[ls] + GetRank(rs, m + 1, r, x);
            }
        }
    
        //查询最小的x,使得<=x的元素个数>=rk(第rk小)
        int GetValue(int o, int l, int r, int rk) {
            if(l == r) {
                return l;
            } else {
                int m = l + r >> 1;
                if(cnt[ls] >= rk)
                    return GetValue(ls, l, m, rk);
                else
                    return GetValue(rs, m + 1, r, rk - cnt[ls]);
            }
        }
    #undef ls
    #undef rs
    } st;
    

    注意:这里面传入的参数都应该是离散化之后的值。权值线段树不能用一次递归实现GetPrev()和GetNext(),需要通过给离散化的值做出一些修改,然后组合GetRank()和GetNext()才可以实现。优势在于代码短,常数小。

    动态开点权值线段树

    不再需要提前离散化了。

  • 相关阅读:
    黑域,黑阈 Permission denied
    手机闪存速度测试工具,AndroBench
    找进程的窗口Handle
    nginx http 正向代理
    windows命令行netstat 统计连接数
    FLIR ONE PRO热成像仪
    python2.0_s12_day14_jQuery详解
    python2.0_s12_day13_javascript&Dom&jQuery
    ssh&scp指定密钥
    apache+php生产环境错误记录
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11877098.html
Copyright © 2020-2023  润新知