• 线段树入门之成段更新


    • 作者:zifeiy
    • 标签:线段树

    HDU1698 Just a Hook

    #include <bits/stdc++.h>
    using namespace std;
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    const int maxn = 100010;
    int sum[maxn<<2], lazy[maxn<<2];
    inline void push_up(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }
    inline void push_down(int rt, int len) {
        if (lazy[rt]) { // 只有在有延迟标记(说明之前全区间覆盖过)才生效
            int l_len = len-len/2, r_len = len/2;
            lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
            sum[rt<<1] = l_len * lazy[rt];
            sum[rt<<1|1] = r_len * lazy[rt];
            lazy[rt] = 0;
        }
    }
    void build(int l, int r, int rt) {
        lazy[rt] = 0;
        if (l == r) {
            sum[rt] = 1;
            return;
        }
        int m = (l + r) >> 1;
        build(lson); build(rson); push_up(rt);
    }
    void update(int L, int R, int val, int l, int r, int rt) {
        if (L <= l && r <= R) {
            lazy[rt] = val;
            sum[rt] = (r - l + 1) * val;
            return;
        }
        push_down(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (R > m) update(L, R, val, rson);
        push_up(rt);
    }
    int T, n, m, a, b, c;
    int main() {
        scanf("%d", &T);
        for (int cas = 1; cas <= T; cas ++) {
            scanf("%d%d", &n, &m);
            build(1, n, 1);
            while (m --) {
                scanf("%d%d%d", &a, &b, &c);
                update(a, b, c, 1, n, 1);
            }
            printf("Case %d: The total value of the hook is %d.
    ", cas, sum[1]);
        }
        return 0;
    }
    

    POJ3468 A Simple Problem with Integers

    #include <cstdio>
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    const int maxn = 100010;
    long long sum[maxn<<2], lazy[maxn<<2];
    inline void push_up(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }
    inline void push_down(int rt, int len) {
        if (lazy[rt]) {
            int l_len = len-len/2, r_len = len/2;
            lazy[rt<<1] += lazy[rt];
            lazy[rt<<1|1] += lazy[rt];
            sum[rt<<1] += l_len * lazy[rt];
            sum[rt<<1|1] += r_len * lazy[rt];
            lazy[rt] = 0;
        }
    }
    void build(int l, int r, int rt) {
        lazy[rt] = 0;
        if (l == r) {
            scanf("%lld", &sum[rt]);
            return;
        }
        int m = (l + r) >> 1;
        build(lson); build(rson); push_up(rt);
    }
    void update(int L, int R, long long val, int l, int r, int rt) {
        if (L <= l && r <= R) {
            lazy[rt] += val;
            sum[rt] += (r - l + 1) * val;
            return;
        }
        push_down(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (R > m) update(L, R, val, rson);
        push_up(rt);
    }
    long long query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) return sum[rt];
        push_down(rt, r-l+1);
        int m = (l + r) >> 1;
        long long res = 0;
        if (L <= m) res += query(L, R, lson);
        if (R > m) res += query(L, R, rson);
        return res;
    }
    int n, q, a, b, c;
    char op[2];
    int main() {
        scanf("%d%d", &n, &q);
        build(1, n, 1);
        while (q --) {
            scanf("%s", op);
            if (op[0] == 'C') {
                scanf("%d%d%d", &a, &b, &c);
                update(a, b, c, 1, n, 1);
            } else {
                scanf("%d%d", &a, &b);
                printf("%lld
    ", query(a, b, 1, n, 1));
            }
        }
        return 0;
    }
    

    POJ2528 Mayor's posters

    题目链接:http://poj.org/problem?id=2528
    题目大意:给你一个无限长的板子,然后依次往上面贴n张等高的海报,问你最后能看到多少张海报。
    思路分析:线段树区间更新问题,但是要注意,给的长度的可能非常大,有1e9,不加处理直接维护一个线段树肯定会MLE,TLE,但是我们注意到一共最多只有2e4个点,因此我们可以用离散化的思想先对区间进行预处理。
    但是注意简单的离散化可能会出现错误,给出下面两个简单的例子应该能体现普通离散化的缺陷:
    例子一: 1-10 1-4 5-10
    例子二: 1-10 1-4 6-10
    普通离散化后都变成了 [1,4][1,2][3,4]
    线段2覆盖了 [1,2] ,线段3覆盖了 [3,4] ,那么线段1是否被完全覆盖掉了呢?
    例子一是完全被覆盖掉了,而例子二没有被覆盖
    解决的办法则是对于距离大于1的两相邻点,中间再插入一个点,本题还用到了Lazy标记的思想
    直接更新区间进行标记而先不对子节点进行处理,如果需要往下更新再将标记下传一层。
    实现代码如下(我的代码里直接把数值存放在了延迟标记里):

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    const int maxn = 100010;
    long long lazy[maxn<<2];
    inline void push_down(int rt, int len) {
        if (lazy[rt]) {
            int l_len = len-len/2, r_len = len/2;
            lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
            lazy[rt] = 0;
        }
    }
    void build(int l, int r, int rt) {
        lazy[rt] = 0;
        if (l == r) return;
        int m = (l + r) >> 1;
        build(lson); build(rson);
    }
    void update(int L, int R, int val, int l, int r, int rt) {
        if (L <= l && r <= R) {
            lazy[rt] = val;
            return;
        }
        push_down(rt, r-l+1);
        int m = (l + r) >> 1;
        if (L <= m) update(L, R, val, lson);
        if (R > m) update(L, R, val, rson);
    }
    int ans;
    bool vis[maxn];
    void query(int l, int r, int rt) {
        if (l == r || lazy[rt]) {
            if (lazy[rt] && !vis[ lazy[rt] ]) { vis[ lazy[rt] ] = true; ans ++; }
            return;
        }
        int m = (l + r) >> 1;
        query(lson); query(rson);
    }
    int T, n, l[maxn], r[maxn], num[maxn], mp[10000010], sz, cnt;
    int main() {
        scanf("%d", &T);
        while (T --) {
            scanf("%d", &n);
            cnt = ans = 0;
            for (int i = 1; i <= n; i ++) vis[i] = false;
            for (int i = 1; i <= n; i ++) {
                scanf("%d%d", &l[i], &r[i]);
                num[cnt++] = l[i];
                num[cnt++] = r[i];
            }
            sort(num, num+cnt);
            cnt = unique(num, num+cnt) - num;
            sz = 0;
            for (int i = 0; i < cnt; i ++) {
                if (i > 0 && num[i-1]+1 < num[i]) mp[ num[i]-1 ] = sz ++;
                mp[ num[i] ] = sz ++;
            }
            build(0, sz-1, 1);
            for (int i = 1; i <= n; i ++) {
                int lnum = mp[ l[i] ];
                int rnum = mp[ r[i] ];
                update(lnum, rnum, i, 0, sz-1, 1);
            }
            query(0, sz-1, 1);
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    POJ3225 区间

    题目链接:http://poj.org/problem?id=3225 本题有中文题面。
    题目大意:区间操作,交、并、补等。
    思路:
    我们对操作进行分析得:

    • U:把区间 ([l, r]) 覆盖成 1;
    • I:把区间 ((- infty , l))((r, + infty)) 覆盖成 0;
    • D:把区间 ([l, r]) 覆盖成 0;
    • C:把区间 ((- infty , l))((r, + infty)) 覆盖成 0,且区间 ([l, r]) 0/1互换;
    • S:把区间 ([l,r]) 0/1互换。

    成段覆盖的操作很简单,比较特殊的就是区间 0/1 互换这个操作,我们可以称之为 异或操作。

    对于懒惰标记:

    • 覆盖标记:我们用0或者1来表示区间内是否都包含0或者1,用-1表示该区间内即包含1又包含0;
    • 异或标记:是否进行了异或操作。

    性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了。
    所以当一个节点得到覆盖标记时需要把异或标记清空;
    而当一个节点得到异或标记的时候,先判覆盖标记,如果是0或者1,直接改变一下覆盖标记,不然的话改变异或标记。

    开区间的话只要数字乘以2就可以处理了(偶数表示端点,奇数表示两端点之间的区间)

    线段树功能:

    • update:成段更新,区间异或;
    • query:简单hash

    代码:略。

    练习

  • 相关阅读:
    浅谈Huffman树
    CF884D:Boxes And Balls
    MySQL单表查询(重要)
    MySQL字段完整性约束(重要)
    MySQL数据类型(重要)
    数据库基本操作
    MySQL权限管理
    MySQL存储引擎概述
    数据库基础
    并发编程小结
  • 原文地址:https://www.cnblogs.com/codedecision/p/11687527.html
Copyright © 2020-2023  润新知