线段树学习笔记

以前学过,但是忘了,重修


线段树的特点是可以通过二分来维护区间元素,使得部分区间操作可以一次完成,无需分别访问区间内元素

这要求区间内元素可以合并,例如区间的和等于区间内元素的和

所以线段树可以维护带有结合律的元素

参考模板题:P3372
核心机制:懒标记
主要函数:

  • build建树
  • update区间修改
  • qsum区间求和
  • pushup合并子节点数值
  • pushdown下放懒标记

pushdown函数:(重要)

void pushdown(int p, int l, int r){//下放懒标记 
	int mid = (r + l) >> 1;
	sum[p << 1] += tag[p] * (mid - l + 1);
	tag[p << 1] += tag[p];//左儿子
	sum[(p << 1) + 1] += tag[p] * (r - mid - 1 + 1);
	tag[(p << 1) + 1] += tag[p];//右儿子
	tag[p] = 0; //清除
}

pushup函数:

void pushup(int p){//合并子节点信息
	sum[p] = sum[p << 1] + sum[(p << 1) + 1];
}

update函数:

void update(int p, int l, int r, int x, int y, ll add){
	if(x <= l && r <= y){//使用懒标记
		sum[p] += add * (r - l + 1);
		tag[p] += add;
		return ;
	}
	pushdown(p, l, r);//下放懒标记
	int mid = (r + l) >> 1;
	if(mid >= x) update(p << 1, l, mid, x, y, add);//左儿子更新
	if(mid + 1 <= y) update((p << 1) + 1, mid + 1, r, x, y, add);//右儿子更新
	pushup(p);//合并子节点数值
}

qsum函数:

ll qsum(int p, int l, int r, int x, int y){//与update相比,无数值修改,无pushup
	ll res = 0;
	if(x <= l && r <= y){//该节点区间完全包含,直接返回值
		return sum[p];
	}
	pushdown(p, l, r);//下方懒标记
	int mid = (r + l) >> 1;
	if(mid >= x) res += qsum(p << 1, l, mid, x, y);//计算左儿子
	if(mid + 1 <= y) res += qsum((p << 1) + 1, mid + 1, r, x, y);//计算右儿子
	return res;
}

build函数:

void build(int p, int l, int r){//O(nlogn)建树,无pushdown
	tag[p] = 0; 
	if(l == r){
		sum[p] = a[l];
		return ;
	}
	int mid = (r + l) >> 1;
	build(p << 1, l, mid);
	build((p << 1) + 1, mid + 1, r);
	pushup(p);
}

完整代码:

#include <bits/stdc++.h>
#define ll long long
#define MAX 100005
using namespace std;
int n, m;
ll a[MAX];//初始数据 
ll sum[MAX << 2];//节点的和 
ll tag[MAX << 2];//节点的懒标记 
void pushup(int p){//合并子节点信息 
	sum[p] = sum[p << 1] + sum[(p << 1) + 1]; 
}
void pushdown(int p, int l, int r){//下放懒标记 
	int mid = (r + l) >> 1;
	sum[p << 1] += tag[p] * (mid - l + 1);
	tag[p << 1] += tag[p];
	sum[(p << 1) + 1] += tag[p] * (r - mid - 1 + 1);
	tag[(p << 1) + 1] += tag[p];
	tag[p] = 0; 
}
void build(int p, int l, int r){//初始线段树
	tag[p] = 0; 
	if(l == r){
		sum[p] = a[l];
		return ;
	}
	int mid = (r + l) >> 1;
	build(p << 1, l, mid);
	build((p << 1) + 1, mid + 1, r);
	pushup(p);
}
void update(int p, int l, int r, int x, int y, ll add){
	if(x <= l && r <= y){
		sum[p] += add * (r - l + 1);
		tag[p] += add;
		return ;
	}
	pushdown(p, l, r);
	int mid = (r + l) >> 1;
	if(mid >= x) update(p << 1, l, mid, x, y, add);
	if(mid + 1 <= y) update((p << 1) + 1, mid + 1, r, x, y, add);
	pushup(p);
}
ll qsum(int p, int l, int r, int x, int y){
	ll res = 0;
	if(x <= l && r <= y){
		return sum[p];
	}
	pushdown(p, l, r);
	int mid = (r + l) >> 1;
	if(mid >= x) res += qsum(p << 1, l, mid, x, y);
	if(mid + 1 <= y) res += qsum((p << 1) + 1, mid + 1, r, x, y);
	return res;
}
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%lld", &a[i]);
	}
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int type = 0;
		scanf("%d", &type);
		if(type == 1){
			int x, y; ll add;
			scanf("%d%d%lld", &x, &y, &add);
			update(1, 1, n, x, y, add);
		}
		else{
			int x, y;
			scanf("%d%d", &x, &y);
			printf("%lld\n", qsum(1, 1, n, x, y));
		}
	}
	return 0;
}
posted @ 2025-06-23 08:34  LIGHTB  阅读(27)  评论(0)    收藏  举报