线段树

Posted tecode

tags:

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

递归

#include <bits/stdc++.h>
#define LL long long
#define Pi acos(-1.0)
#define INF 2147483646
#define eps 1e-9
#define MS 100009
#define mss 17
using namespace std;
// Notice the data size

LL n; 			// 记录原数组个数 
LL a[MS]; 		// 存原数组 
LL p[MS*4]; 	// 数组实现树形结构 ,每一个节点对应一段区间 
LL la[MS*4]; 	// 懒惰标记 

void input() {
	// 存储原数组 
	for(int i=1; i<=n; i++) cin >> a[i];
}

// 向上更新信息 
void push_up(LL rt) {
	p[rt] = p[rt<<1] + p[rt<<1|1];
}

// 下推标记 
void push_down(LL rt, LL nl, LL nr) {
	// 如果被标记
	if(la[rt]){
		// 下推标记
		la[rt<<1] = la[rt<<1] + la[rt]; 
		la[rt<<1|1] = la[rt<<1|1] + la[rt];
		// 更新左右孩子信息
		p[rt<<1] = p[rt<<1] + nl*la[rt];
		p[rt<<1|1] = p[rt<<1|1] + nr*la[rt];
		// 清除标记
		la[rt] = 0;
	} 
}

// 建树 
void build(LL l, LL r, LL rt) {
	if(l == r){
		// 在 *rt* 节点存储数据 
		p[rt] = a[l];
		return; 
	} 
	LL m = (l+r)/2;
	// 左右递归 寻找最后的叶子 
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1); 
	// 向上更新信息
	push_up(rt);
}

// 区间修改 (+)
void section_revise(LL L, LL R, LL l, LL r, LL x, LL rt) {
	if(L<=l && r<=R){
		// 修改该节点对应区间的值 
		p[rt] = p[rt] + x*(r-l+1);
		// 修改懒惰标记 
		la[rt] = la[rt] + x;
		return;
	}
	LL m = (l+r)/2;
	// 下推标记
	// 参数:节点, 左,右孩子元素个数
	push_down(rt,m-l+1,r-m); 
	// 左右递归 ,寻找包含区间 
	if(m>=L) section_revise(L,R,l,m,x,rt<<1);
	if(m< R) section_revise(L,R,m+1,r,x,rt<<1|1);
	//  向上更新信息
	push_up(rt); 
}

// 区间求和 
LL section_sum(LL L, LL R, LL l, LL r, LL rt) {
	// 若查询到包含区间 
	if(L<=l && r<=R){ 
		return p[rt];
	}
	LL m = (l+r)/2;
	// 下推标记
	push_down(rt,m-l+1,r-m);
	// 记录答案 
	LL ans = 0;
	if(m>=L) ans += section_sum(L,R,l,m,rt<<1);
	if(m< R) ans += section_sum(L,R,m+1,r,rt<<1|1);
	return ans;
}

// 数组现状输出 
void output(LL l, LL r, LL rt){
	if(l == r){
		cout << p[rt] << " ";
		return;
	}
	LL m = (l+r)/2;
	// 下推标记
	push_down(rt,m-l+1,r-m);
	// 左右递归 寻找最后的叶子 
	output(l,m,rt<<1);
	output(m+1,r,rt<<1|1); 
	
}

int main() {
	// 输入数据并建树 
	cin >> n;
	input();
	build(1,n,1);
	// 区间修改 
	LL l ,r ,x;
	cin >> l >> r >> x; 
	section_revise(l,r,1,n,x,1);
	// 输出修改后结果
	output(1,n,1); 
	cout << endl;
	// 区间求和
	cin >> l >> r;
	cout << section_sum(l,r,1,n,1) << endl; 
	

	return 0;
}

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

线段树

CCF(除法):线段树区间修改(50分)+线段树点修改(100分)+线段树(100分)

线段树合并

数据结构——线段树

论线段树:二

线段树