线段树模板(史上最简单易懂的数据结构)
什么是线段树?
线段树言下之意就是将线段分解为一颗树,并且这颗树是一颗二叉树,树上的每一个节点都具有某些性质。
线段树有什么用?
线段树主要能够在O(logn)的时间复杂度上解决区间上的问题。
题目引入:AcWing243.一个简单的整数问题2
本题目就是很简单的线段树的板子题,可以用来当做模板来使用。
线段树的几个重要操作:
1.tree[]数组的建立:
struct node{
int l;//表示左端点
int r;//表示右端点
int v;//表示区间内的总和
int lazy;//作为懒标记,表示子节点还未被父亲节点更新,当查询或者询问的时候一定要向下pushdown。
}tree[N*4];
2.built操作(建树)
如何将一个线段建成树是实现线段树的重要操作。请看代码与图1:
void built(int l,int r,int x){
if(l==r){
tree[x]={l,r,a[l]};
return;
}
else tree[x]={l,r};
int mid=(l+r)/2;
built(l,mid,x*2);
built(mid+1,r,x*2+1);
pushup(x);//注意这里一定要回溯
}
3.modify操作(修改操作)
void modify(int x,int l,int r,int value){
if(tree[x].l>=l&&tree[x].r<=r){//如果当前节点的值在询问的值中间,则直接返回
tree[x].v+=(tree[x].r-tree[x].l+1)*value;
tree[x].lazy+=value;
return;
}
pushdown(x); //至于这里为什么要pushdown,以后在说
int mid=(tree[x].l+tree[x].r)/2;
if(l<=mid)modify(x*2,l,r,value);
if(r>mid)modify(x*2+1,l,r,value);//注意这的大于小于号
pushup(x);
}
这里修改操作能够减小时间复杂度的地方就是能够一整块的相加,例如:
我在区间[4,8]内都加上10,则相当于将节点[4,5],[6,8]的lazy+10,然后value加上长度*lazy,并且向上更新pushup,如图2:
这里为什么在修改的时候也要pushdown,是因为当我们修改值的时候,一定是要求其祖先的lazy值都需要为0。如果不为0的话,就会出现向上更新的时候还没有来的及将子节点更新,这样的话查询出来的值会比真实值要小。
4.qurey(询问操作)
int qurey(int l,int r,int x){
if(tree[x].l>=l&&tree[x].r<=r){//如果当前节点的区间在询问节点之内,则直接返回
return tree[x].v;
}
pushdown(x);//同样的,询问的时候也要pushdown,将处理子节点
int mid=(tree[x].l+tree[x].r)/2;
int v=0;
if(l<=mid)v+=qurey(l,r,x*2);//分别向左,右子节点寻找
if(r>mid)v+=qurey(l,r,x*2+1);
return v;
}
同样的,每当我询问的时候,我们就找到那个区间内值进行累加,并且需要pushdown
5.pushup(向上更新操作)
void pushup(int x){//这里的意思就是当前节点的值是由子节点的值更新过来的
tree[x].v=tree[x*2].v+tree[x*2+1].v;
}
6.pushdown(向下传递数据)
void pushdown(int x){//这里的pushdown操作主要是向下传递lazy
tree[x*2].v+=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
tree[x*2+1].v+=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
tree[x*2].lazy+=tree[x].lazy;
tree[x*2+1].lazy+=tree[x].lazy;
tree[x].lazy=0;
}
因为本题目的样例会很大,所以采用long long 来存数据:
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+7;
struct node{
ll l;
ll r;
ll v;
ll lazy;
}tree[N*4];
ll a[N];
void pushup(ll x){
tree[x].v=tree[x*2].v+tree[x*2+1].v;
}
void pushdown(ll x){
tree[x*2].v+=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
tree[x*2+1].v+=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
tree[x*2].lazy+=tree[x].lazy;
tree[x*2+1].lazy+=tree[x].lazy;
tree[x].lazy=0;
}
void built(ll l,ll r,ll x){
if(l==r){
tree[x]={l,r,a[l]};
return;
}
else tree[x]={l,r};
ll mid=(l+r)/2;
built(l,mid,x*2);
built(mid+1,r,x*2+1);
pushup(x);
}
void modify(ll x,ll l,ll r,ll value){
if(tree[x].l>=l&&tree[x].r<=r){
tree[x].v+=(tree[x].r-tree[x].l+1)*value;
tree[x].lazy+=value;
return;
}
pushdown(x);
ll mid=(tree[x].l+tree[x].r)/2;
if(l<=mid)modify(x*2,l,r,value);
if(r>mid)modify(x*2+1,l,r,value);
pushup(x);
}
ll qurey(ll l,ll r,ll x){
if(tree[x].l>=l&&tree[x].r<=r){
return tree[x].v;
}
pushdown(x);
ll mid=(tree[x].l+tree[x].r)/2;
ll v=0;
if(l<=mid)v+=qurey(l,r,x*2);
if(r>mid)v+=qurey(l,r,x*2+1);
return v;
}
int main(){
ll n,m;
cin>>n>>m;
for(ll i=1;i<=n;i++){
cin>>a[i];
}
built(1,n,1);
for(ll i=1;i<=m;i++){
char a;
cin>>a;
if(a=='Q'){
ll x,y;
cin>>x>>y;
cout<<qurey(x,y,1)<<endl;
}
else{
ll x,y,z;
cin>>x>>y>>z;
modify(1,x,y,z);
}
}
return 0;
}
进阶线段树:洛谷 线段树
AC代码:
#include<bits/stdc++.h>
using namespace std;
const long long N=1e6+7;
typedef long long ll;
ll a[N];
ll n,p;
struct node
{
ll l;
ll r;
ll v;
ll lazyadd;
ll lazymul;
}tree[N*4];
void pushup(ll x){
tree[x].v=tree[x*2].v%p+tree[x*2+1].v%p;
}
void eval(node &t, int add, int mul)
{
t.v = ((ll)t.v * mul + (ll)(t.r - t.l + 1) * add) % p;
t.lazymul = (ll)t.lazymul* mul % p;
t.lazyadd = ((ll)t.lazyadd * mul + add) % p;
}
void pushdown(ll x){
eval(tree[x << 1], tree[x].lazyadd, tree[x].lazymul);
eval(tree[x << 1 | 1], tree[x].lazyadd, tree[x].lazymul);
tree[x].lazyadd=0;
tree[x].lazymul=1;
}
void built(ll l,ll r,ll x)
{
if(l==r)
{
tree[x]={l ,r ,a[l],0,1};
return;
}
tree[x]={l ,r,0,0,1};
ll mid = (l + r) /2;
built(l ,mid ,x*2);
built(mid+1 ,r ,x*2+1);
pushup(x);
}
void modifyadd(ll l,ll r,ll value,ll x){
if(tree[x].l>=l&&tree[x].r<=r){
eval(tree[x],value,1);
return;
}
pushdown(x);
ll mid=(tree[x].l+tree[x].r)/2;
if(l<=mid)modifyadd(l,r,value,x*2);
if(r>mid)modifyadd(l,r,value,x*2+1);
pushup(x);
}
void modifymul(ll l,ll r,ll value,ll x){
if(tree[x].l>=l&&tree[x].r<=r){
eval(tree[x],0,value);
return;
}
pushdown(x);
ll mid=(tree[x].l+tree[x].r)/2;
if(l<=mid)modifymul(l,r,value,x*2);
if(r>mid)modifymul(l,r,value,x*2+1);
pushup(x);
}
ll query(ll l,ll r,ll x){
if(tree[x].l>=l&&tree[x].r<=r){
return tree[x].v%p;
}
pushdown(x);
ll mid=(tree[x].l+tree[x].r)/2;
ll v=0;
if(l<=mid)v+=query(l,r,x*2);
if(r>mid)v+=query(l,r,x*2+1);
return v%p;
}
int main(){
ll m;
cin>>n>>m>>p;
for(ll i=1;i<=n;i++){
cin>>a[i];
}
built(1,n,1);
for(ll i=1;i<=m;i++){
ll x;
cin>>x;
if(x==1){
ll y,z,s;
cin>>y>>z>>s;
modifymul(y,z,s,1);
}
else if(x==2){
ll y,z,s;
cin>>y>>z>>s;
modifyadd(y,z,s,1);
}
else {
ll y,z;
cin>>y>>z;
cout<<query(y,z,1)%p<<endl;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!