线段树的区间修改主要有三种:
成段增减, 成段替换,区间异或
区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是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);
}