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

线段树 区间修改

时间:2019-01-20 13:45:10      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:ace   操作   节点   cin   覆盖   div   print   class   区间查询   

我们对于线段树的区间修改你可以用一个最傻的办法循环进行单点修改(时间复杂度太高十分麻瓜)所以,我们要用一个聪明的做法延迟标记(LAZY)

在限度拿书的“区间查询”指令中,每当遇到被询问区间[l,r]完全覆盖的节点时,可以立即把该节点上存储的信息作为候选答案返回。已经有大佬证明,被询问区间[l,r]在线段树上会被分成O(logN)个小区间(节点)从而在O(logN)的时间内求出答案。

我们在执行修改指令时,同样可以在 L<= Pl <= Pr <= R 的情况下立即返回,只不过在回溯之前向节点P增加一个标记,标识“该节点曾经被修改过,但其子节点尚未被更新”。

如果在后续的指令中,需要从节点P向下递归,我们再检查P是否具有标记。若有标记,就根据标记信息更新P的两个子节点,同时为P的两个子节点增加标记,然后清除P的标记。

也就是说,除了在修改指令中直接划分成的O(logN)个节点之外,对任意给点的修改都延迟到“在后续操作中递归进入他的父亲节点时”再执行。这样一来,每条查询或修改指令的时间复杂度都降低到了O(logN)。这些标记被称为“延迟标记”。延迟标记提供了线段树中从上往下传递信息的方式。这种“延迟”也是设计算法与解决问题的一个重要思路。

附上代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
struct segment_tree{
	int l,r;
	long long sum,mark;
}tree[maxn*4];
long long a[maxn];
int n,m;
void pushup(int root){
	tree[root].sum = tree[root<<1].sum+tree[root<<1|1].sum;
}
void pushdown(int root){
	if(tree[root].mark){
		tree[root<<1].sum += tree[root].mark*(tree[root<<1].r-tree[root<<1].l+1);
		tree[root<<1|1].sum += tree[root].mark*(tree[root<<1|1].r-tree[root<<1|1].l+1);
		tree[root<<1].mark += tree[root].mark;
		tree[root<<1|1].mark += tree[root].mark;
		tree[root].mark = 0;
	}
}
void build(int L,int R,int root){
	tree[root].l = L;
	tree[root].r = R;
	if(L == R){
		tree[root].sum = a[L];
		return;
	}
	int mid = (L+R)>>1;
	build(L,mid,root<<1);
	build(mid+1,R,root<<1|1);
	pushup(root);
}
void update(int L,int R,int al,int ar,int root,long long w){
	if(R < al || L > ar)return;
	if(al <= L && R <= ar){
		tree[root].sum += w*(R-L+1);
		tree[root].mark += w;
		return;
	}
	pushdown(root);
	int mid = (L+R)>>1;
	update(L,mid,al,ar,root<<1,w);
	update(mid+1,R,al,ar,root<<1|1,w);
	pushup(root);
}
long long query(int L,int R,int al,int ar,int root){
	if(R < al || L > ar)return 0;
	if(al <= L && R <= ar)return tree[root].sum;
	pushdown(root);
	int mid = (L+R)>>1;
	return query(L,mid,al,ar,root<<1)+query(mid+1,R,al,ar,root<<1|1);
}
int main(){
	cin>>n>>m;
	for(int i = 1;i <= n;i++)scanf("%lld",&a[i]);
	build(1,n,1);
	for(int i = 1,op,x,y;i <= m;i++){
		long long k;
		scanf("%d",&op);
		if(op == 1)scanf("%d%d%lld",&x,&y,&k),update(1,n,x,y,1,k);
		if(op == 2)scanf("%d%d",&x,&y),printf("%lld\n",query(1,n,x,y,1));
	}
	return 0;
}

  

线段树 区间修改

标签:ace   操作   节点   cin   覆盖   div   print   class   区间查询   

原文地址:https://www.cnblogs.com/wangyifan124/p/10294433.html

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