线段树 区间修改

Posted wangyifan124

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树 区间修改相关的知识,希望对你有一定的参考价值。

我们对于线段树的区间修改你可以用一个最傻的办法循环进行单点修改(时间复杂度太高十分麻瓜)所以,我们要用一个聪明的做法延迟标记(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
",query(1,n,x,y,1));
	}
	return 0;
}

  

以上是关于线段树 区间修改的主要内容,如果未能解决你的问题,请参考以下文章

模板线段树-单点修改,区间查询

P3372 模板线段树 1(区间修改区间查询)(树状数组)

线段树区间修改区间求和

线段树 :区间修改,区间查询

数据结构线段树 (定义 & 点修改/区间查询)

HDU - 4578 Transformation(线段树区间修改)