线段树学习笔记
以前学过,但是忘了,重修
线段树的特点是可以通过二分来维护区间元素,使得部分区间操作可以一次完成,无需分别访问区间内元素
这要求区间内元素可以合并,例如区间的和等于区间内元素的和
所以线段树可以维护带有结合律的元素
参考模板题: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;
}

浙公网安备 33010602011771号