线段树模板(史上最简单易懂的数据结构)

什么是线段树?

线段树言下之意就是将线段分解为一颗树,并且这颗树是一颗二叉树,树上的每一个节点都具有某些性质。

线段树有什么用?

线段树主要能够在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;
} 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值