线段树详解--(处理区间问题)

线段树是擅长处理区间问题的,是一颗完美二叉树根据节点中维护的数据的不同,可以提供不同的功能。可以结合图和代码中的注释进行理解。
线段树详解--(处理区间问题)_第1张图片
图有点丑,自己画的
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;
}



你可能感兴趣的:(线段树)