线段树是擅长处理区间问题的,是一颗完美二叉树根据节点中维护的数据的不同,可以提供不同的功能。可以结合图和代码中的注释进行理解。
图有点丑,自己画的
1,建树
struct Node {
int sum, lazy;
}node[maxn<<2];
int n, num[maxn];
void update(int root){//区间最小值
node[root].sum =min(node[root<<1].sum, node[root<<1|1].sum);
}
void update(int root){//区间最大值
node[root].sum =max(node[root<<1].sum, node[root<<1|1].sum);
}
void update(int root) {//区间和
node[root].sum = node[root<<1].sum + node[root<<1|1].sum;
}
void build_tree(int root, int l, int r) {//根节点和两个区间
if(l == r) {//是否为叶子节点
node[root].sum = num[l];
return ;
}
int mid = l + r >> 1;
build_tree(root<<1, l, mid);//左子树
build_tree(root<<1|1, mid+1, r);//右子树
//root<<1|1是位运算跟root*2+1一样
update(root);//对根节点的处理,求和,求积,最大值,最小值等
//不停向上维护
}
2,点修改
首先由根节点1向下递归,找到对应的叶节点,
然后,修改叶节点的值,
再向上返回,在函数返回的过程中,更新路径上的节点的统计信息。
void change(int root, int l,int r, int pos, int va) {
if(l == r) {
node[root].sum = va;
return ;
}
int mid = l + r >> 1;
if(pos <=mid) change(root<<1, l, mid, pos, va);//如果pos<=当前区间的中点,说明我想找的[pos,pos]区间,在左半边
else change(root<<1, mid+1, r, pos, va);
update(root);//沿路回溯。回溯到的点root,都是被[pos,pos]区间影响到的点,边回溯边更新
}
3,区间修改
区间修改需要用到lazy_tag(懒惰标记),这是向下延迟修改,但是向上显示的信息是修改以后的信息,所以查询的时候可以得到正确的结果。对于要修改的区间,可以先不忙修改,而是lazy标记,需要用到时再进行向下传递参数。
void pushdown(int root,int l,int r) {
if(node[root].lazy == 0) return ;//如果此节点根本没有lazy_tag,直接返回,不作处理
int chl = root<<1;
int chr = root<<1|1;
int mid = l + r>>1;
//更新sum的值
node[chl].sum += node[root].lazy * (mid-l+1);
node[chr].sum += node[root].lazy * (r-mid);
//lazy下推到子节点
node[chl].lazy += node[root].lazy;
node[chr].lazy += node[root].lazy;
node[root].lazy = 0;
}
void change(int root, int l, int r, int ql,int qr, int va) {
if(l == ql && qr == r) {
node[root].sum += va * (r-l+1);//sum更新为正确值
node[root].lazy += va;//lazy标记
return ;
}
pushdown(root, l ,r);//向下传递
//每次都是先下推,再更新,从而保证,计算root的时候,它的左右两子树都已经是正确值,左右两子树都不存在lazy更新延迟,都已经更新好了。
int mid = l + r>> 1;
if(qr <= mid) change(root<<1, l, mid, ql, qr,va);
else if(ql > mid) change(root<<1|1, mid+1, r, ql, qr,va);
else {
change(root<<1, l, mid, ql, mid, va);
change(root<<1|1, mid+1, r, mid+1, qr,va);
}
update(root);
}
4,查询
int query(int root, int l,int r, int ql, int qr) {//查询[ql,qr]的值
if(l == ql && r== qr) {
return node[root].sum;
}
pushdown(root, l, r);
int mid = l + r>> 1;
if(qr <= mid) return query(root<<1, l, mid, ql, qr);
else if(ql > mid) return query(root<<1|1, mid+1, r, ql, qr);
else return query(root<<1, l, mid, ql, mid) + query(root<<1|1, mid+1, r, mid+1, qr);
}
下面给出完整代码供参考
#include
using namespace std;
const int maxn = 1e5+100;
typedef pair<int,int> pii;
const int INF = 0x3f3f3f3f;
struct Node {
int sum, lazy;
}node[maxn<<2];
int n, num[maxn];
void update(int root) {
node[root].sum = node[root<<1].sum + node[root<<1|1].sum;
}
void build_tree(int root, int l, int r) {
// node[root].sum = INT_MIN;
node[root].lazy = 0;
if(l == r) {
node[root].sum = num[l];
return ;
}
int mid = l + r >> 1;
build_tree(root<<1, l, mid);
build_tree(root<<1|1, mid+1, r);
update(root);
}
//void change(int root, int l,int r, int pos, int va) {
// if(l == r) {
// node[root].sum = va;
// return ;
// }
//
// int mid = l + r >> 1;
// if(pos <=mid) change(root<<1, l, mid, pos, va);
// else change(root<<1, mid+1, r, pos, va);
// update(root);
//}
void pushdown(int root,int l,int r) {
if(node[root].lazy == 0) return ;
int chl = root<<1;
int chr = root<<1|1;
int mid = l + r>>1;
node[chl].sum += node[root].lazy * (mid-l+1);
node[chr].sum += node[root].lazy * (r-mid);
node[chl].lazy += node[root].lazy;
node[chr].lazy += node[root].lazy;
node[root].lazy = 0;
}
void change(int root, int l, int r, int ql,int qr, int va) {
if(l == ql && qr == r) {
node[root].sum += va * (r-l+1);
node[root].lazy += va;
return ;
}
pushdown(root, l ,r);
int mid = l + r>> 1;
if(qr <= mid) change(root<<1, l, mid, ql, qr,va);
else if(ql > mid) change(root<<1|1, mid+1, r, ql, qr,va);
else {
change(root<<1, l, mid, ql, mid, va);
change(root<<1|1, mid+1, r, mid+1, qr,va);
}
update(root);
}
int query(int root, int l,int r, int ql, int qr) {
if(l == ql && r== qr) {
return node[root].sum;
}
pushdown(root, l, r);
int mid = l + r>> 1;
if(qr <= mid) return query(root<<1, l, mid, ql, qr);
else if(ql > mid) return query(root<<1|1, mid+1, r, ql, qr);
else return query(root<<1, l, mid, ql, mid) + query(root<<1|1, mid+1, r, mid+1, qr);
}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d", &num[i]);
build_tree(1, 1, n);
int m; scanf("%d", &m);
while(m--) {
char s[10]; scanf("%s",s);
if(strcmp(s, "change") == 0) {
// int pos, va; scanf("%d%d", &pos, &va);
// change(1, 1, n, pos, va);
int l, r, va; scanf("%d%d%d", &l, &r, &va);
change(1, 1, n, l, r, va);
} else {
int l, r; scanf("%d%d", &l, &r);
printf("%d", query(1, 1,n ,1, r));
}
}
return 0;
}