线段树模板

Posted zmachine

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树模板相关的知识,希望对你有一定的参考价值。

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 kk。
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 [x, y][x,y] 内每个数加上 kk。
  2. 2 x y:输出区间 [x, y][x,y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;

const int maxn = 1e5+5;
LL n,m;
//n数列数字的个数,m为操作的总个数
LL num[maxn],ans[maxn<<2],tag[maxn<<2];
//num[i]存储原始序列的值, 


//返回左孩子 
LL ls(LL p){
    return p<<1;
}

//返回右孩子 
LL rs(LL p){
    return p<<1|1;
}

void push_up(LL p){
    ans[p] = ans[ls(p)]+ans[rs(p)];
}//不断向上进行相应的更新操作

void build(LL p,LL l,LL r){
    //p表示区间段节点的编号 
    
    tag[p] =0;
    //lazy tag的初始化处理部分 
    if(l==r){
        ans[p] = num[l];
        return;
    }
    //左右区间相等,到达叶子节点部分,被赋予实际的值 
    
    
    //借助二叉树,二分来降低相应的时间复杂度 
    LL mid = (l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    
    push_up(p);//通过子节点来维护父节点的相应的值,在回溯的时候来进行相应的实现 
} 


//区间修改部分的处理 

//对于区间内的每个数均加k操作
 
 
 //f用来记录当前节点所代表的区间信息,然后辅助向下进行相应的更新 
void f(LL p,LL l,LL r,LL k){
    tag[p] = tag[p]+k;
    
    ans[p] += k*(r-l+1);//区间内元素个数*k 
} 

void push_down(LL p,LL l,LL r){
    //将tag信息传递到其子节点,并将自身节点的信息清零    
    
    LL mid = (l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    
    tag[p] = 0;
    //每次更新两个儿子节点的信息,并将相应的信息继续往下传递 
}



//注意单点修改是 
void update(LL nl,LL nr,LL l,LL r, LL p,LL k){
    //nl-nr为需要进行修改的区间
    //l-r当前节点存储的区间
    //p,k节点的编号和需要改变的增量 
    
    if(nl<=l&&nr>=r){
        //当前区间被需要更行的区间包含
        
        //tag的优势显示在这,达到递归的终结位置 
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return; 
    }
    push_down(p,l,r);
    //将更改信息下传下去
    
    LL mid = (l+r)>>1;
    if(nl<=mid) update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    
    //回溯的时候更新ans的值
    push_up(p); 
     
    
}



//查询操作
LL query(LL q_x,LL q_y,LL l,LL r,LL p){
    LL res = 0;
    if(q_x<=l && q_y>=r) return ans[p];
    //当前节点表示的区间被包含
    
    LL mid = (l+r)>>1;
    push_down(p,l,r);//将之前没有处理的lazy tag向下传递,进行更新并求解
    
    if(q_x<=mid) res+=query(q_x,q_y,l,mid,ls(p));
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
    return res; 
} 
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&num[i]);
    }
    build(1,1,n);
    //以1为根节点,开始建立相应的数 
    LL choice,x,y,k;
    while(m--){
        scanf("%lld",&choice);
        switch(choice){
            case 1:
                scanf("%lld%lld%lld",&x,&y,&k);
                update(x,y,1,n,1,k);
                break;
            case 2:
                scanf("%lld%lld",&x,&y);
                printf("%lld\\n",query(x,y,1,n,1));
                
        }
    }
    return 0;
} 

 

以上是关于线段树模板的主要内容,如果未能解决你的问题,请参考以下文章

线段树模板整理

线段树模板总结

线段树模板

模板线段树-单点修改,区间查询

P3834 模板可持久化线段树 1(主席树)

模板 线段树(部分功能)