线段树学习笔记
1 线段树初步
1.1 区间操作
1.1.1 已知的区间操作
- 前缀和 —— 区间查询
- 差分 —— 区间修改
- ST表 —— 区间查询
没有可以同时进行修改和查询的!!!
1.1.2 能让ST表实现修改吗?
计算影响:
\(eg\).
- 修改 \(input[1]\) : \(st[1][1]\) ~ \(st[1][log_2 n]\) —— \(log_2 n\) 次修改
- 修改 \(input[2]\) : \(st[1][2]\) ~ \(st[1][log_2 n]\) ,\(st[2][1]\) ~ \(st[2][log_2 n]\) —— \(2 log_2 n\) 次修改
- 修改 \(input[n]\) : \(st[1][log_2 n]\),……,\(st[n][1]\) ~ \(st[n][log_2 n]\) —— \(n log_2 n\) 次修改
≈ 重写 ST表!!!
1.2 线段树理论
1.2.1 线段树的结构

每个节点的特点:
- 每个节点是一个区间
- 每个节点存储 左端点,右端点,区间答案
1.2.2 线段树的存储
标号存储:
1.左子结点序号为 \(((id)<<1)\)
2.右子结点序号为 \(((id)<<1|1)\)
注意:线段树数组应该开 n*4
1.3 单修区查线段树的实现
注: 以下代码为此题的单点加数,区间求和为例
1.3.1 建树(build)
递归构建左右子树
//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)
void build(int id,int l,int r){
if(l==r){
tree[id]=input[l];
return ;
}
int m=(l+r)>>1;
build(ls(id),l,m);
build(rs(id),m+1,r);
// 标记
return ;
}
标记处是不是少了什么?
我们只构建了叶子节点,其他节点都是空的!
设计一个 “上传更新” 函数
//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)
void pushup(int id){
tree[id]=tree[ls(id)]+tree[rs(id)];
return ;
}
void build(int id,int l,int r){
if(l==r){
tree[id]=input[l];
return ;
}
int m=(l+r)>>1;
build(ls(id),l,m);
build(rs(id),m+1,r);
pushup(id);
return ;
}
1.3.2 修改(update)
递归寻找需要修改的节点
//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)
void pushup(int id){
tree[id]=tree[ls(id)]+tree[rs(id)];
return ;
}
void update(int id,int l,int r,int q,int v){
if(l==r){
tree[id]+=v;
return ;
}
int m=(l+r)>>1;
if(q<=m) update(ls(id),l,m,q,v);
else update(rs(id),m+1,r,q,v);
pushup(id);
return ;
}
1.3.3 查询(query)
当询问区间完全包含当前区间时,返回当前区间答案
//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)
void pushup(int id){
tree[id]=tree[ls(id)]+tree[rs(id)];
return ;
}
int query(int id,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
return tree[id];
}
int m=(l+r)>>1,ans=0;
if(ql<=m) ans+=query(ls(id),l,m,ql,qr);
if(qr>m) ans+=query(rs(id),m+1,r,ql,qr);
return ans;
}
1.3.4 完整 AC 代码
点击查看代码
#include<bits/stdc++.h>
#define ls(id) (id<<1)
#define rs(id) (id<<1|1)
using namespace std;
const int maxn=1e6+5;
int tree[maxn*4],input[maxn];
void pushup(int id){
tree[id]=tree[ls(id)]+tree[rs(id)];
return ;
}
void build(int id,int l,int r){
if(l==r){
tree[id]=input[l];
return ;
}
int m=(l+r)>>1;
build(ls(id),l,m);
build(rs(id),m+1,r);
pushup(id);
return ;
}
void update(int id,int l,int r,int q,int v){
if(l==r){
tree[id]+=v;
return ;
}
int m=(l+r)>>1;
if(q<=m) update(ls(id),l,m,q,v);
else update(rs(id),m+1,r,q,v);
pushup(id);
return ;
}
int query(int id,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
return tree[id];
}
int m=(l+r)>>1,ans=0;
if(ql<=m) ans+=query(ls(id),l,m,ql,qr);
if(qr>m) ans+=query(rs(id),m+1,r,ql,qr);
return ans;
}
int main(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>input[i];
}
build(1,1,n);
for(int i=1;i<=q;i++){
int type,x,y;
cin>>type>>x>>y;
if(type==1)
update(1,1,n,x,y);
else
cout<<query(1,1,n,x,y)<<"\n";
}
return 0;
}

浙公网安备 33010602011771号