[C++]线段树 区间修改 区间查询

线段树 区间修改 区间查询

请先阅读上一篇Bolg

算法思想

这次要引入一个核心变量:

lazy 懒标记

为了达到区间修改的目的

又为了减少运算量

所以就需要引入懒标记这个变量

用来满足 即用即推

没有用到的时候便以懒标记的形式存在线段中

子线段要用了便向下推行 \(lazy\)

举个例子:

p1

如果我们只用红色这个线段

那就不用向下推

\(lazy\) 留在这里

p2

但是如果要用到橙色的部分

那就需要把 \(lazy\) 下推到橙色部分

代码实现

变量含义详见上个Bolg

add函数

void add(int i,int l,int r,int k){
	if(l <= tree[i].l && tree[i].r <= r){
		tree[i].sum += k * (tree[i].r - tree[i].l + 1);
		tree[i].lazy += k;
		return ;
	}
	if(tree[i].lazy != 0) push(i);
	if(tree[li].r >= l)
		add(li,l,r,k);
	if(tree[ri].l <= r)
		add(ri,l,r,k);
	tree[i].sum = tree[li].sum + tree[ri].sum;
	return ;
}

我们会发现这个函数会调用到 push函数

因此我先解释一下 push函数 的作用:

将当前的 lazy标记 推到下一层

然后我们再来看代码

  • \(l <= tree[i].l\) && \(tree[i].r <= r\)

这表示当前线段已经完全在范围包裹里了

\(sum\) 的操作:

 把 \(k\) 的值分配到当前线段

 有 \(tree[i].r - tree[i].l + 1\) 个数

 那 \(k\) 就需要乘上 \(tree[i].r - tree[i].l + 1\)

\(lazy\) 的操作:

 直接加上 \(k\)

 要注意这里的 \(lazy\) 是上一轮 add留下来的懒标记

 (如果 \(lazy\) 不为 0 的话)

 因为不需要向下推

 所以在原来的基础上直接加就行了

  • \(tree[i].lazy != 0\)

如果运行到这里

说明区间和线段有交叉的部分

那我们就需要二分然后往下了

这时候 \(lazy\) 就不能再留在上一层了

需要用 \(push函数\)\(lazy\) 推下去

怎么推会在后面讲 这里可以先这样理解

  • \(tree[li].r >= l\)
  • \(tree[ri].l <= r\)

这里二分向下推的操作和之前相同

便不再赘叙

当然 最后还需要吧 \(sum\) 的值更新

push函数

void push(int i){
	tree[li].lazy += tree[i].lazy;
	tree[ri].lazy += tree[i].lazy;
	int mid_ = (tree[i].l + tree[i].r) >> 1; 
	tree[li].sum += tree[i].lazy * (mid_ - tree[li].l + 1);
	tree[ri].sum += tree[i].lazy * (tree[i].r - mid_);
	tree[i].lazy = 0;
	return ;
}

前面有提到这个函数时用来下推 \(lazy\)

那这里就来看看它是如何做到的

首先先把两个子线段的 \(lazy\) 复制为当前线段的懒标记

再把 \(sum\) 给加上

然后让线段的 \(lazy\) 归为 0

search函数

int search(int i,int l,int r){
	if(l <= tree[i].l && tree[i].r <= r)
		return tree[i].sum;
	push(i);
	int ans = 0;
	if(tree[li].r >= l) ans += search(li,l,r);
	if(tree[ri].l <= r) ans += search(ri,l,r);
	return ans;
}
  • \(l <= tree[i].l\) && \(tree[i].r <= r\)

完全在区间内就直接返回 \(sum\)

  • \(tree[li].r >= l\)
  • \(tree[ri].l <= r\)

如果要往下面搜索

那就先要把 \(lazy\) 往下面推

然后在把这段的值加起来返回上去

Code

#include<bits/stdc++.h>
#define maxn 1000010
#define INF 1e12
#define mid ((l+r)>>1)
#define li i<<1
#define ri 1+(i<<1)
using namespace std;

int n,val[maxn];

struct Node{
	int l,r,sum;
	int k,lazy;
}tree[maxn];

void Read(){
	cin >> n;
	for(int i = 1;i <= n;i++)cin >> val[i];
}

void build(int i,int l,int r){
	tree[i].l = l;
	tree[i].r = r;
	if(l == r){
		tree[i].sum = val[l];
		return ;
	}
	build(li,l,mid);
	build(ri,mid+1,r);
	tree[i].sum = tree[li].sum + tree[ri].sum;
	return ;
}

void push(int i){
	tree[li].lazy += tree[i].lazy;
	tree[ri].lazy += tree[i].lazy;
	int mid_ = (tree[i].l + tree[i].r) >> 1; 
	tree[li].sum += tree[i].lazy * (mid_ - tree[li].l + 1);
	tree[ri].sum += tree[i].lazy * (tree[i].r - mid_);
	tree[i].lazy = 0;
	return ;
}

void add(int i,int l,int r,int k){
	if(l <= tree[i].l && tree[i].r <= r){
		tree[i].sum += k * (tree[i].r - tree[i].l + 1);
		tree[i].lazy += k;
		return ;
	}
	if(tree[i].lazy != 0) push(i);
	if(tree[li].r >= l)
		add(li,l,r,k);
	if(tree[ri].l <= r)
		add(ri,l,r,k);
	tree[i].sum = tree[li].sum + tree[ri].sum;
	return ;
}

int search(int i,int l,int r){
	if(l <= tree[i].l && tree[i].r <= r)
		return tree[i].sum;
	push(i);
	int ans = 0;
	if(tree[li].r >= l) ans += search(li,l,r);
	if(tree[ri].l <= r) ans += search(ri,l,r);
	return ans;
}

void interaction(){
	while(1){
		int tot;
		cin >> tot;
		if(tot == 1){
			int l,r;
			cin >> l >> r;
			cout << search(1,l,r) << endl;
		} else if(tot == 2){
			int l,r,k;
			cin >> l >> r >> k;
			add(1,l,r,k);
		} else if(tot == 3){
			return ;
		}
	}
}

int main(){
	cout << "query section" << endl << "change section" << endl;
	Read();
	build(1,1,n);
	cout << "query 1" << endl << "change 2" << endl << "break 3" << endl;
	interaction();
	return 0;
}
posted @ 2021-03-30 19:20  Rosyr  阅读(202)  评论(0编辑  收藏  举报