首页 > 技术文章 > 线段树

aprincess 2019-10-05 06:10 原文

1结构
线段树是一个平衡的二元树,所有叶子到根的距离最多只差1。令整个区间的长度为N,则其有N个叶节点,每个叶节点代表一个单位区间,每个内部结点代表的区间为其两个儿子代表区间的联集。

2基本操作
线段树所要提供的是查询一个区间内的资讯,并允许修改操作。要使用线段树,此资讯必须满足对于区间与位于区间内的一点,要可以由、求得。例如范围最值查询即符合此条件。

代码中, rt指的是root, 当前子树的根节点; l, r指的是当前子树所统计的区间利用完全二叉堆的性质来保存节点编号, 所以rt << 1是左子树的节点, rt << 1 | 1是右子树的节点在查询和成端更新操作中的L和R是指修改或者查询的区间


//线段树 
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N=2e5+9; 
int n,m;
ll sum[N<<2]/*4倍空间*/,tag[N<<2];


//将子节点的值更新到父节点
void pushup(int u){
    sum[u]=sum[u<<1]+sum[u<<1|1];
}

void buildtree(int u,int ul,int ur){
    if(ul==ur){//如果是叶节点 
        scanf("%ll",sum+u);
        return;
    }
    int mid=ul+ur>>1;
    buildtree(u<<1,ul,mid);
    buildtree(u<<1|1,mid+1,ur);
    pushup(u);
}

void update(int u,int ul,int ur,int mx){
    tag[u]+=mx;
    sum[u]+=(ll)mx*(ur-ul+1);
}


//节点懒惰标记下传
void pushdown(int u,int ul,int ur){
    if(tag[u]){
        int mid=ul+ur>>1;
        update(u<<1,ul,mid,tag[u]);
        update(u<<1|1,mid+1,ur,tag[u]);
        tag[u]=0;
    }
}

void modify(int u,int ul,int ur,int ml,int mr,int mx){
    if(ml<=ul&&ur<=mr){
        update(u,ul,ur,mx);
        return;
    }
    pushdown(u,ul,ur);
    int mid=ul+ur>>1;
    if(ml<=mid)
        modify(u<<1,ul,mid,ml,mr,mx);
    if(mr>=mid+1)
        modify(u<<1|1,mid+1,ur,ml,mr,mx);
    pushup(u);
}

ll query(int u,int ul,int ur,int ml,int mr){
    if(ml<=ul&&ur<=mr)
        return sum[u];
    pushdown(u,ul,ur);
    int mid=ul+ur>>1;
    ll s=0;
    if(ml<=mid)
        s+=query(u<<1,ul,mid,ml,mr);
    if(mr>=mid+1)
        s+=query(u<<1|1,mid+1,ur,ml,mr);
    return s;
}

int main(){
    scanf("%d%d",&n,&m);
    buildtree(1,1,n);
    while(m--){
        int opt,x,y,z;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==0){
            scanf("%d",&z);
            modify(1,1,n,x,y,z);//修改函数 
        } else{
            printf("%ll\n",query(1,1,n,x,y));
        } 
    }
}
 

 

推荐阅读