线段树代码模板(改)
设计思想:
- 将线段数组tr[i] 原本表示的当前区间存储的数据的个数改用一个结构体来表示当前区间的左右端点和区间数据的和。
- 数据数组T[i] 和线段数组的叶子节点一一对应。
- 关键函数:pushup 通过当前区间的左右子区间来更新当前区间数据的和
Bulid 因为线段树单位区间与T数据数组下标一一对应,直接递归切割线段,当切割得单位线段时,该节点的sum就是对应的T的值,此时单位线段所表示的值==T里面的下标
Query 通过二分的思想和分割再求和的思想,将要查询的区间在逻辑角度上分割成多段在分别求和
Update通过tr[i] 表示的当前区间的左右端点与区间和。来递归找到与要修改的T中的位置的对应的tr中的位置,修改该叶子节点,然后再通过左右子区间更新区间的和。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int num = 100010;
int n , m ;
int T[num];//记录数据
struct XBT{
int l , r ;
int sum;
}tr[4*num];
void pushup(int idx){ //更新区间存储的数据的和 因为下标是从1开始
tr[idx].sum=tr[idx<<1].sum + tr[idx<<1|1].sum;//大的区间有其左右子区间合并得到
return;
}
void build(int idx ,int lef ,int rig ){//初始化树,因为树的叶子节点和数据数组下标一一对应初始化时直接递归就行了
if(lef==rig){//单位区间
tr[idx] = {lef ,rig,T[lef]};//p为要插入的数据在T数组中的位置
return;
}
else{
int mid = lef+rig >>1;//求中点的位置
build(idx*2,lef,mid);
build(idx*2+1,mid+1,rig);
tr[idx]={lef,rig};
pushup(idx);
}
return ;
}
int query(int idx ,int lef , int rig){
if(tr[idx].l>=lef&&tr[idx].r<=rig) return tr[idx].sum;//如果当前节点所表示的区间在要查询的区间里面的话则直接返回该区间的和
int mid= tr[idx].l+tr[idx].r>>1;//需要用来判断查找区间与当前区间的左右子区间有无交集
int sum = 0 ;
if(lef <= mid) sum+=query(idx<<1,lef,rig);//判断与左区间是否有交集,加上交集部分的和
if(rig > mid) sum+=query(idx<<1|1,lef,rig);//判断与右区间是否有交集,加上交集部分的和
return sum;//得到查询区间的和
}
void update(int idx,int x ,int add_n){//idx 当前节点的位置,x要修改的位置,add_n增加的值
if(tr[idx].l==tr[idx].r){//如果到达叶子节点直接更改sum的值
tr[idx].sum+=add_n;
return;
}
else{
int mid = tr[idx].l+tr[idx].r>>1;//用来判断要修改的位置在当前线段树的左区间还是右区间
if(x<=mid) update(idx<<1,x,add_n);//表示在左区间
else{
update(idx<<1|1,x,add_n);
}
pushup(idx);//更新当前区间的数据
}
}
int main(){
cin >> n >> m ;
for(int i =1 ; i <= n ; i++){
cin >> T[i];
}
build(1,1,n);
while(m--){
int k , a, b;
scanf("%d %d %d ",&k,&a,&b);
if(k==0){
cout << query(1,a,b)<<endl;
}
else{
update(1,a,b);
}
}
return 0;
}