线段树入门之成段更新

  • 作者: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.\n", 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\n", 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\n", 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

代码:略。

练习

原文地址:https://www.cnblogs.com/codedecision/p/11687527.html

时间: 2024-11-12 20:38:39

线段树入门之成段更新的相关文章

POJ3468 A Simple Problem with Integers 【线段树】+【成段更新】

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 57666   Accepted: 17546 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

Count the Colors(线段树之区间成段更新)

萌萌哒的传送门 这道题目是线段树区间成段更新的应用,我们只需在建立线段树时从原来的左右儿子不相连,改为相连即可以解决此类问题. 如从原来的[l,mid] , [mid + 1,r] 改为 [l,mid],[mid,r]即可; /********************* * zoj1610 * * 线段树的区间成段更新 * 延迟标记 * *********************/ #include <iostream> #include <cstring> #include &l

POJ2528 Mayor&#39;s posters 【线段树】+【成段更新】+【离散化】

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 39795   Accepted: 11552 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

HDU1698 Just a Hook 【线段树】+【成段更新】+【lazy标记】

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 15889    Accepted Submission(s): 7897 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

[HDOJ4027]Can you answer these queries?(线段树,特殊成段更新,成段查询)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 RT,该题要求每次更新是更新所有节点,分别求平方根,查询是求和.昨晚思前想后找有没有一个数学上的开平方的和等于和的开平方之类的规律.但是想了想发现这不就是小的时候,如果你这么想那老师就会骂死你的那个- -! 所以显然这个题是无法按套路成段更新了,懒惰标记也是没有用了,我们应该去按照区间更新每一个节点.结果TLE了一发,这说明这题不是这么搞,一定还有规律的.注意到题目给数据规模是2^63以及题目

树状数组成段更新——POJ 3468

A Simple Problem with IntegersCrawling in process... Crawling failed Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3468 Description You have N integers, A1, A2, ... , AN. You need to deal wit

hdu 1698 Just a Hook 线段树成段更新

线段树功能:update:成段替换 成段更新去要用到延迟标记,具体调试代码就容易懂些 #include <iostream> #include <string> #include <cstdio> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 using namespace std; const int MAXN = 111111; int sum[MAXN<<2], ch

Hdu1698 Just a Hook(线段树成段更新)

题意很简单:1-n个钩子初始价值是1,然后题目给出Q个操作,x y z,将x->y的钩子价值改为z,最后输出n个钩子的总价值. 线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息) //3160 KB 624 ms #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define M 100005 #define

poj3468A Simple Problem with Integers(线段树+成段更新)

题目链接: huangjing题意: 给n个数,然后有两种操作. [1]Q a b 询问a到b区间的和. [2]C a b c将区间a到b的值都增加c. 思路: 线段树成段更新的入门题目..学会使用lazy即可.还需要注意的是,lazy的时候更改是累加,而不是直接修改..有可能连续几次进行修改操作..注意这一点就好了... 题目: Language: Default A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: