線段樹標記永久化


線段樹的標記永久化

其實線段樹的標記永久化是一個非常容易理解的東西,往往我們都會在區間操作時打lazytag,但是在標記下放時會耗費大量的時間,所以我們可以嘗試標記永久化,這樣我們的就不用下放標記,同時代碼也更加簡潔,因為我們少了一個pushdown函數,同時出錯率也會大大降低。

首先我們要學會普通的線段樹

這里我們以前在線段樹時是使用lazytag來解決區間問題,但是我們會發現在lazytag容易出錯,所以我們可以考慮標記永久化,思路也十分清晰

對於標記永久化,其實和普通線段樹比起來,其實差不多

下面就是一個維護區間和的代碼( Luogu3372 【模板】線段樹 1)

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;
int n,m;
ll sum[4000001],tag[4000001]; 


void pushup(int o){
	sum[o]=sum[o<<1]+sum[o<<1|1]; 
}

void build(int o,int l,int r){
	if(l==r){
		scanf("%lld",&sum[o]);
		return;
	}
	int mid=(l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);
}

void update(int o,int l,int r,int x,int y,ll v){
	sum[o]+=((ll)min(r,y)-(ll)max(x,l)+1)*v;
	if(x<=l and r<=y){
		tag[o]+=v;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid){
		update(o<<1,l,mid,x,y,v);
	}
	if(y>mid){
		update(o<<1|1,mid+1,r,x,y,v);
	}
}

ll query(int o,int l,int r,ll tg,int x,int y){
	if(x<=l and r<=y){
		return sum[o]+(ll)(min(r,y)-max(x,l)+1)*(tg);
	}
	int mid=(l+r)>>1;
	ll ret=0;
	if(x<=mid){
		ret+=query(o<<1,l,mid,tg+tag[o],x,y);
	}
	if(y>mid){
		ret+=query(o<<1|1,mid+1,r,tg+tag[o],x,y);
	}
	return ret;
}

int main(){
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int opt;
		scanf("%d",&opt);
		if(opt==1){
			int x,y;
			ll z;
			scanf("%d%d%lld",&x,&y,&z);
			update(1,1,n,x,y,z);
		}else{
			int x,y;
			scanf("%d%d",&x,&y);
			printf("%lld\n",query(1,1,n,0,x,y));
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM