操作:
操作1: 单点修改 O(log n)
操作2:取件查询 O(log n)
对于节点x
父节点:(x/2) x>>1
左儿子:2x x<<1
右儿子: 2x+1 x<<1 | 1
构造核心函数:
1、pushup(u):用子节点信息来更新当前节点信息(把信息往上传递)
2、build(u,l,r):在一段区间上初始化线段树,其中u表示根结点,l表示左边界,r表示右边界
3、query(u,l,r):查询某段区间的和,其中u表示根结点,l表示左边界,r表示右边界
4、modify(u,x,v):修改操作,在u结点中,x位置加上v
具体例子:AcWing 1264. 动态求连续区间和
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=100010;
int n,m;
int w[N];
struct Node//构造节点记录线段树各个节点的sum
{
int l,r;
int sum;
}tr[4*N];
void pushup(int u)//向上传递 用子节点信息来更新当前节点信息
{
tr[u].sum=tr[u<<1].sum+tr[u<<1 | 1].sum;
}
void build(int u,int l,int r)//在一段区间上初始化线段树,其中u表示根结点,l表示左边界,r表示右边界
{
if(l==r) tr[u]={l,r,w[r]};//已经是叶节点
else
{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1| 1,mid+1,r);//递归构造左右子节点区间
pushup(u);
}
}
int query(int u,int l,int r)//查询某段区间的和,其中u表示根结点,l表示左边界,r表示右边界
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
int mid=tr[u].l+tr[u].r>>1;
int sum=0;
if(l<=mid) sum=query(u<<1,l,r);
if(r>mid) sum+=query(u<<1|1,l,r);
return sum;
}
void modify(int u,int x,int v)//修改操作,在u结点中,x位置加上v
{
if (tr[u].l == tr[u].r) tr[u].sum += v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);//确定x节点所在区间
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
int main( )
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
int k, a, b;
while (m -- )
{
scanf("%d%d%d", &k, &a, &b);
if (k == 0) printf("%d\n", query(1, a, b));
else modify(1, a, b);
}
return 0;
}