线段树学习(二)区间修改

线段树的区间修改主要有三种:

成段增减, 成段替换,区间异或

区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn),例如当我们要更新区间[0,3]内的叶子节点时,需要更新出了叶子节点3,9外的所有其他节点。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。

延迟标记每个节点新增加一个标记,记录这个节点是否进行了某种修改(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。

成段替换:

例题:HDU 1698 Just a Hook 这是一个区间求和加上成段替换,只需记录最大长度所以不需query,sum[1]便可


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 100000 + 10;
int col[maxn<<2], sum[maxn<<2];

void pushup(int root){
    sum[root] = sum[root<<1] + sum[root<<1|1];
}

void pushdown(int root, int l, int r){
    if (col[root]){ //延迟标记向下更新
        col[root<<1] = col[root<<1|1] = col[root];
        int m = (l+r) >> 1;
        sum[root<<1] = (m-l+1)*col[root];
        sum[root<<1|1] = (r-m)*col[root];
        col[root] = 0;//消除标记
    }
}

void build(int root, int l, int r){
    col[root] = 0;//初始化为0
    sum[root] = 1;
    if (l == r) return;

    int m = (l+r)>>1;
    build(root<<1, l, m);
    build(root<<1|1, m+1, r);
    pushup(root);
}

void update(int root, int l, int r, int ql, int qr, int key){
    if (ql<=l && r<=qr){
        col[root] = key;
        sum[root] = key*(r-l+1);
        return;
    }
    pushdown(root, l, r);//向下更新
    int m = (l+r)>>1;
    if (ql<=m) update(root<<1, l, m, ql, qr, key);
    if (qr>m) update(root<<1|1, m+1, r, ql, qr, key);
    pushup(root); //改变之后向上更新
}

int main(){
    int T;
    scanf("%d", &T);
    for (int i = 1; i<=T; i++){
        int n, m;
        scanf("%d %d", &n, &m);
        build(1, 1, n);
        while (m--){
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            update(1, 1, n, a, b, c);
        }
        printf("Case %d: The total value of the hook is %d.\n", i, sum[1]);
    }
    return 0;
}

成段增减:

例题: POJ 3468 A Simple Problems  of Integers


成段增减,区间求和, 代码类似于成段替换,只是在于pushdown和update有略微区别


void pushdown(int root, int l, int r){
    if (col[root]){
        col[root<<1] += col[root]; col[root<<1|1] += col[root];//区别 在于 = 变成 +=
        int m = (l+r) >> 1;
        sum[root<<1] += (m-l+1)*col[root];
        sum[root<<1|1] += (r-m)*col[root];
        col[root] = 0;
    }
}

void update(int root, int l, int r, int ql, int qr, int key){
    if (ql<=l && r<=qr){
        col[root] += key; //区别
        sum[root] += key*(r-l+1);
        return;
    }
    pushdown(root, l, r);
    int m = (l+r)>>1;
    if (ql<=m) update(root<<1, l, m, ql, qr, key);
    if (qr>m) update(root<<1|1, m+1, r, ql, qr, key);
    pushup(root);
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值