您的位置:首页 > 健康 > 美食 > 线段树 Segment Tree

线段树 Segment Tree

2024/10/6 0:38:01 来源:https://blog.csdn.net/taoyiwei17_QFOI/article/details/141192675  浏览:    关键词:线段树 Segment Tree

使用场景

  1. 对数列进行区间询问(包括最值、求和、乘积等询问)。

  2. 对数列进行区间修改(统一赋值、增减)。

使用思想

分治。

详解

手造一段数列:

2 5 9 1 7 6 5 3

给定 m 次操作:

type1. 将区间 [L,R]加上 val。

type2. 询问区间[L,R] 的元素和。

算法实现

  1. 建一棵线段树

  2. 查询函数

  3. 修改函数

时间复杂度分析

  1. 线段树结点编号达到 4×n。

  2. build O(n)

  3. query O(log n​)

  4. update O(n)(未优化)

未优化代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int a[N],tree[4*N], n, m;
void pushup(int cur)
{tree[cur]=tree[2*cur]+tree[2*cur+1];return ;
}
void build(int cur, int lt, int rt)
{if(lt==rt){tree[cur]=a[lt];return ;}int mid=(lt+rt)>>1;build(cur*2,lt,mid);build(cur*2+1,mid+1,rt);pushup(cur);return ;
}
int query(int cur, int lt, int rt, int qx, int qy)
{if(qy<lt||qx>rt){return 0;}if(qx<=lt&&rt<=qy){return tree[cur];}int mid=lt+rt>>1;return query(cur*2,lt,mid,qx,qy)+query(cur*2+1,mid+1,rt,qx,qy);
}
void update(int cur, int lt, int rt, int qx, int qy, int val)
{if(qy<lt||qx>rt){return ;}if(lt==rt){tree[cur]+=val;return ;}int mid=lt+rt>>1;update(cur*2,lt,mid,qx,qy,val);update(cur*2+1,mid+1,rt,qx,qy,val);pushup(cur);
}
signed main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];	}build(1,1,n);while(m--){int opt, x, y, val;cin>>opt>>x>>y;if(opt==1){cin>>val;update(1,1,n,x,y,val);}if(opt==2){cout<<query(1,1,n,x,y)<<"\n";}}
}

线段树的优化

很容易发现,单次修改的时间复杂度很慢。

考虑优化,采用懒标记。

性质:线段树的修改是为询问而服务的。

维护标记 tag_{cur} 表示结点 cur 需要修改的值。

修改后 update 函数单次时间复杂度可达 O(log n​)

优化后代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int a[N], n, m, tag[4*N], tree[4*N];
void pushup(int cur)
{tree[cur]=tree[cur*2]+tree[cur*2+1];return ;
}
void addtag(int cur, int lt, int rt, int val)
{tag[cur]+=val;tree[cur]+=(rt-lt+1)*val;return ;
}
void pushdown(int cur, int lt, int rt)
{if(tag[cur]==0){return ;}int mid=lt+rt>>1;addtag(cur*2,lt,mid,tag[cur]);addtag(cur*2+1,mid+1,rt,tag[cur]);tag[cur]=0;return ;
} 
void build(int cur, int lt, int rt)
{if(lt==rt){tree[cur]=a[lt];return ;}int mid=lt+rt>>1;build(cur*2,lt,mid);build(cur*2+1,mid+1,rt);pushup(cur);return ;
}
int query(int cur, int lt, int rt, int qx, int qy)
{if(qy<lt||qx>rt){return 0;}if(qx<=lt&&rt<=qy){return tree[cur];}pushdown(cur,lt,rt);int mid=lt+rt>>1;return query(cur*2,lt,mid,qx,qy)+query(cur*2+1,mid+1,rt,qx,qy);
}
void update(int cur, int lt, int rt, int qx, int qy, int val)
{if(qy<lt||qx>rt){return ;}if(qx<=lt&&rt<=qy){addtag(cur,lt,rt,val);return ;}pushdown(cur,lt,rt);int mid=lt+rt>>1;update(cur*2,lt,mid,qx,qy,val);update(cur*2+1,mid+1,rt,qx,qy,val);pushup(cur);return ;
}
signed main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}build(1,1,n);while(m--){int opt, x, y, val;cin>>opt>>x>>y;if(opt==1){cin>>val;update(1,1,n,x,y,val);}else{cout<<query(1,1,n,x,y)<<"\n";}}return 0;
}

带乘法的线段树

多维护一个乘法标记,随加法标记更新,注意运算顺序。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int a[N], n, m, tag[4*N], tree[4*N], mod, mul[4*N];
void pushup(int cur)
{tree[cur]=tree[cur*2]%mod+tree[cur*2+1]%mod;return ;
}
void addtag(int cur, int lt, int rt, int val)
{tag[cur]+=val;tree[cur]+=(rt-lt+1)*val%mod;return ; 	
}
void addtag1(int cur, int lt, int rt, int val)
{tag[cur]=tag[cur]*val%mod;mul[cur]=mul[cur]*val%mod;tree[cur]=tree[cur]*val%mod;return ;
}
void pushdown(int cur, int lt, int rt)
{if(tag[cur]==0&&mul[cur]==1){return ;}int mid=(lt+rt)>>1;addtag1(cur*2,lt,mid,mul[cur]);addtag1(cur*2+1,mid+1,rt,mul[cur]);addtag(cur*2,lt,mid,tag[cur]);addtag(cur*2+1,mid+1,rt,tag[cur]);tag[cur]=0;mul[cur]=1;return ;
} 
void build(int cur, int lt, int rt)
{if(lt==rt){tree[cur]=a[lt];return ;}int mid=lt+rt>>1;build(cur*2,lt,mid);build(cur*2+1,mid+1,rt);pushup(cur);return ;
}
int query(int cur, int lt, int rt, int qx, int qy)
{if(qy<lt||qx>rt){return 0;}if(qx<=lt&&rt<=qy){return tree[cur];}pushdown(cur,lt,rt);int mid=lt+rt>>1;return query(cur*2,lt,mid,qx,qy)+query(cur*2+1,mid+1,rt,qx,qy);
}
void update(int cur, int lt, int rt, int qx, int qy, int val)
{if(qy<lt||qx>rt){return ;}if(qx<=lt&&rt<=qy){addtag(cur,lt,rt,val);return ;}pushdown(cur,lt,rt);int mid=lt+rt>>1;update(cur*2,lt,mid,qx,qy,val);update(cur*2+1,mid+1,rt,qx,qy,val);pushup(cur);return ;
}
void update1(int cur, int lt, int rt, int qx, int qy, int val)
{if(qy<lt||qx>rt){return ;}if(qx<=lt&&rt<=qy){addtag1(cur,lt,rt,val);return ;}pushdown(cur,lt,rt);int mid=lt+rt>>1;update1(cur*2,lt,mid,qx,qy,val);update1(cur*2+1,mid+1,rt,qx,qy,val);pushup(cur);return ;
}
signed main()
{cin>>n>>m>>mod;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=4*n;i++){mul[i]=1;}build(1,1,n);while(m--){int opt, x, y, val;cin>>opt>>x>>y;if(opt==2){cin>>val;update(1,1,n,x,y,val);}else if(opt==1){cin>>val;update1(1,1,n,x,y,val);}else{cout<<query(1,1,n,x,y)%mod<<"\n";}}return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com