模板:线段树标记永久化

这篇博客探讨了线段树数据结构中的标记永久化技术,它用于区间修改和查询操作,特别是在处理不可逆操作如区间赋值和最大值维护时。文章通过实例展示了如何在代码中实现线段树,并解释了在特定情况下如何避免懒惰标记的下传,以优化常数。同时,提到了该技术的局限性,例如区间赋值必须大于等于原有值。

为了那不可知的询问永远坚守于此

解析

众所周知线段树的区间修改是需要打懒标记的
多数时候,这个标记在询问子区间时需要下传

而标记永久化,就是指不下传懒标记的一种操作
在某些时候标记不便下传时有所应用
比如zkw线段树二维线段树
然而zkw线段树我并不会
还能起到一定的常数优化作用 (这是真的卡常卡疯了)

这个操作是有局限性的
那就是:不可逆性
比如说
标记永久化可以维护区间赋值和维护最大值
但前提是区间赋值必须不小于区间内原来的值
否则就无法操作

细节: pushup的时候一定要把自己身上的懒标记考虑在内!!

代码

(线段树1)

//#include<bits/stdc++.h>
#include<cstdio>
#include<cctype>
#include<algorithm>

const int N=5e5+100;
const int mod=1e9+7;
#define ll long long
using namespace std;
inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}

int n,m,k;

#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
ll tr[N<<2],laz[N<<2],a[N];
inline void add(int k,int l,int r,ll v){
	laz[k]+=v;tr[k]+=(r-l+1)*v;
	return;
}
inline void pushup(int k,int l,int r){
	tr[k]=tr[ls]+tr[rs]+(r-l+1)*laz[k];
}
void build(int k,int l,int r){
	if(l==r){
		tr[k]=a[l];return;
	}
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(k,l,r);
	return;
}
void change(int k,int l,int r,int x,int y,ll v){
	if(x<=l&&r<=y){
		//printf("add:(%d %d)\n",l,r);
		add(k,l,r,v);return;
	}
	if(x<=mid) change(ls,l,mid,x,y,v);
	if(y>mid) change(rs,mid+1,r,x,y,v);
	pushup(k,l,r);
	return;
}
ll ask(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y) return tr[k];
	ll res=(min(y,r)-max(x,l)+1)*laz[k];
	if(x<=mid) res+=ask(ls,l,mid,x,y);
	if(y>mid) res+=ask(rs,mid+1,r,x,y);
	//printf("(%d %d) res=%lld\n",l,r,res);
	return res;
}
int main(){
	#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	#endif
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		char c;int x,y,z;
		scanf(" %c%d%d",&c,&x,&y);
		if(c=='Q') printf("%lld\n",ask(1,1,n,x,y));
		else{
			z=read();
			change(1,1,n,x,y,z);
		}
	}
	return 0;
}
/*
5 3
7 1 4 1 9 
1 3 5 3
1 1 4 2
2 3 5
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值