码迷,mamicode.com
首页 > 其他好文 > 详细

[知识点]线段树标记永久化

时间:2017-12-08 14:09:47      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:区间修改   strong   原创   注意   部分   复杂   pushd   std   pac   

前言:

本文由Hallmeow原创,转载请注明出处!

由于打丧心病狂的 [BZOJ 4826]影魔  导致需要学习标记永久化,于是入坑OvO

知识点:线段树标记永久化

对于树套树,主席树等使用到线段树的比较复杂的数据结构,如果我们区间修改的话,打标记后pushdown或者pushup是很费劲的

那么我们能不能不用pushdown和pushup呢?当然可以啦!这样就用到标记永久化了!

原理就是: 在路过该节点的时候把修改对答案的影响加上,来省去标记下放的过程

实现起来:

线段树的每个节点维护 sum 与 add 两个标记

修改时:

设区间[xl,xr]全部加v

当目前询问区间与当前区间完全重合的时候,更新add的值,返回。

在一路下来的时候把所有经过的区间(相当于包含询问区间的区间)的sum加上此次修改所产生的影响 v*(xr-xl+1)。

注意完全重合之后就返回了,也就是说下面的部分的影响还没有更新。不要着急

void update(int rt,int l,int r,int v,int xl,int xr){
	sum[rt]+=v*(xr-xl+1);
	if(l==xl&&r==xr){
		add[rt]+=v; return;
	}
	int mid=(l+r)>>1;
	if(xr<=mid)	update(rt<<1,l,mid,v,xl,xr);
	else{
		if(xl>mid)	update(rt<<1|1,mid+1,r,v,xl,xr);
		else update(rt<<1,l,mid,v,xl,mid),update(rt<<1|1,mid+1,r,v,mid+1,xr);
	}
}

询问时:

由于上面的更新没有对下面产生影响,所以我们需要一路累加add,直到目前询问区间与当前区间完全重合的时候,答案为sum+add*区间长度

注意累加add不用累加上完全重合的区间的add,因为它已经在修改的时候对sum进行更新了

int query(int rt,int ad,int l,int r,int xl,int xr){
	if(xl==l&&xr==r){
		return sum[rt]+ad*(xr-xl+1);
	}	
	int mid=(l+r)>>1;
	if(xr<=mid) return query(rt<<1,ad+add[rt],l,mid,xl,xr);
	else{
		if(xl>mid) return query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
		else return query(rt<<1,ad+add[rt],l,mid,xl,mid)+query(rt<<1|1,ad+add[rt],mid+1,r,mid+1,xr);
	}
}

区间修改线段树标记永久化模板

#include<iostream>
#include<cstdio>
#include<cstring>
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 201000
using namespace std;
int n,m;
int sum[N*4],add[N*4];
int a[N];
void build(int l,int r,int rt){
	if(l==r){
		sum[rt]=a[l];return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int rt,int l,int r,int v,int xl,int xr){
	sum[rt]+=v*(xr-xl+1);
	if(l==xl&&r==xr){
		add[rt]+=v; return;
	}
	int mid=(l+r)>>1;
	if(xr<=mid)	update(rt<<1,l,mid,v,xl,xr);
	else{
		if(xl>mid)	update(rt<<1|1,mid+1,r,v,xl,xr);
		else update(rt<<1,l,mid,v,xl,mid),update(rt<<1|1,mid+1,r,v,mid+1,xr);
	}
}
int query(int rt,int ad,int l,int r,int xl,int xr){
	if(xl==l&&xr==r){
		return sum[rt]+ad*(xr-xl+1);
	}	
	int mid=(l+r)>>1;
	if(xr<=mid) return query(rt<<1,ad+add[rt],l,mid,xl,xr);
	else{
		if(xl>mid) return query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
		else return query(rt<<1,ad+add[rt],l,mid,xl,mid)+query(rt<<1|1,ad+add[rt],mid+1,r,mid+1,xr);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	pos(i,1,n) scanf("%d",&a[i]);
	build(1,n,1);
	pos(i,1,m){
		int opt;scanf("%d",&opt);
		int x,y;scanf("%d%d",&x,&y);
		if(opt==1){
			int k;scanf("%d",&k);
			update(1,1,n,k,x,y);
		}
		else printf("%d\n",query(1,0,1,n,x,y));
	}
	return 0;
}

 

[知识点]线段树标记永久化

标签:区间修改   strong   原创   注意   部分   复杂   pushd   std   pac   

原文地址:http://www.cnblogs.com/Hallmeow/p/8004676.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!