线段树入门
- IT业界
- 2025-08-20 14:03:02

对于一个区间进行询问,进行修改,都是用线段树进行处理。
线段树和普通的树不一样,普通的树的节点存的是一个编号,线段树存的是一个区间,而且线段树一定是一棵完全二叉树。例如:
这就是一棵线段树。
例如对于[1...8](也就是1号根节点)这个节点来说,
[1...4](也就是2号节点)是它的lson(左儿子)
[5...8](也就是3号节点)是它的rson(右儿子)
并且和二叉树一样,,(其中p为当前节点)
线段树可以给区间加上某个值 ,乘以某个值,覆盖某个值,取最大值 , 查询,单点查询,区间查询,查询和,查询最大最小值,查询区间最大字段和,查询各种奇怪的东西。(如果两个区间可以合并,所有的信息合并,那么就可以查询.
比如说区间最大字段和。
比如说我们找区间的最大字段和。
设表示区间的最大字段和, 我们已知的所有信息,我们要得到的所有信息。
所以;
其中表示前缀和的最大值,表示后缀和的最大值。
设tot[p]表示这一段的总和。
遍历时就用类似二分的做法枚举区间就行了
(其实蛮简单的)
例题:单点修改,区间查询
一道模板题,详见代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; #define ls p+p//左儿子 #define rs p+p+1//右儿子 long long sum[maxn*4],ans;//记得开4倍 int a[maxn],n,q; void update(int p){ sum[p]=sum[ls]+sum[rs];//因为是加和,所以当前节点为它的左右儿子的和 } void build(int p,int l,int r){//建树,分别表示当前节点编号,区间左端点和右端点 if(l==r) {//如果到了叶子节点 sum[p]=a[l];//初值 return; } int mid=l+r>>1; build(ls,l,mid); build(rs,mid+1,r); update(p); } //a[x]+=v; void change(int p,int l,int r,int x,int v){//更改,参数同上 if(l==r&&l==x){//如果是这个要加的节点 sum[p]+=v; return; } int mid=l+r>>1; if(x<=mid)change(ls,l,mid,x,v); else change(rs,mid+1,r,x,v); update(p);//记得更改 } void query(int p,int l,int r,int x,int y){ if(x<=l&&r<=y) {//如果是这个区间 ans+=sum[p];//加上答案 return; } int mid=l+r>>1; if(x<=mid)query(ls,l,mid,x,y); if(y>mid)query(rs,mid+1,r,x,y); } int main(){ cin>>n>>q; for(int i=1;i <=n;i++)cin>>a[i]; build(1,1,n); while(q--){ int a,b,c; cin>>a>>b>>c; if(a==1)change(1,1,n,b,c); else{ ans=0; query(1,1,n,b,c); cout<<ans<<endl; } } }如果是要求区间最大/值的话,就修改update函数就行了。
那如何进行区间修改呢?
那需要打上lazy标记。
比如只有区间加法。 那么你应该修改change函数和query函数,
并增加一个push操作,用来下传标记。
如果还是不明白懒标记的话,
就去看这个吧
void change2(int p,int l,int r,int x,int y,int z) { if(x<=l&&r<=y){ sum[p]+=(r-l+1)*z;//当前节点应+=区间长度*懒标记 add[p]+=z;//加上懒标记 return; } int mid=l+r>>1; if(x<=mid)change2(ls,l,mid,x,y,z); if(y>mid)change2(rs,mid+1,r,x,y,z); update(p); } void push(int p,int l,int r){//一般的区间加法的标记传递 int mid=l+r>>1; sum[lson]+=(mid-l+1)*lazy[p]; sum[rson]+=(r-mid)*lazy[p]; lazy[ls]+=lazy[p];//记得把标记传递下去。 lazy[rs]+=lazy[p]; lazy[p]=0;//标记使用过后要清空。 } void query(int p,int l,int r,int x){ if(l==r){ 更新答案 return; } push(p,l,r);//传递标记 int mid=l+r>>1; if(x<=mid)query(lson,l,mid,x); else query(rson,mid+1,r,x); }我相信你一定会觉得线段树灰常简单的,对吧?