/*应用:区间维护(查找、赋值等)
/*目标信息应该满足区间加法(异或、数字之和、最大最小值、最大公因数等等)
*/
#include <iostream>
using namespace std;
const int MAXN = 1000007;
int Sum[MAXN << 2], Add[MAXN << 2];
int A[MAXN];
void PushUp(const int rt) { //将该结点的左右儿子和赋值给该结点
Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1];
}
void PushDown(const int rt, int ln, int rn) {
if (Add[rt]) {
Add[rt << 1] += Add[rt];
Add[rt << 1 | 1] += Add[rt];
Sum[rt << 1] += Add[rt] * ln;
Sum[rt << 1 | 1] += Add[rt] * rn;
Add[rt] = 0;
}
}
//建立完全二叉树
void Build(const int l, const int r, const int rt) { //结点为R,R<<1表示左儿子,R<<1|1(R*2+1)表示右儿子
if (l == r) { //二分线段,直到只有一个元素
Sum[rt] = A[l]; //a表示初始数组,默认所有值都为0
return;
}
int m = (l + r) >> 1; //二分法,递归实现
Build(l, m, rt << 1);
Build(m + 1, r, rt << 1 | 1);
PushUp(rt); //从底层向上赋值
}
void UpdateNode(const int L, const int C, const int l, const int r, const int rt) { //向某个结点加上某个值
if (l == r) {
Sum[rt] += C;
return;
}
int m = (l + r) >> 1;
if (L <= m) //二分查找
UpdateNode(L, C, l, m, rt << 1);
else
UpdateNode(L, C, m + 1, r, rt << 1 | 1);
PushUp(rt); //更新父节点的值
}
//更新区间结点,通过只更新上层结点的值来省时间;比如,1-5 -> 1-3, 4-5 -> ...中,要将1-3区间每个节点添加2,便在Sum[]中添加6,Add[]中添加2,然后要便利到1-3其中一个结点时,再把Add[]中的值下沉更新,这就是所谓的PushDown的含义
void UpdateSpan(const int L, const int R, const int C, const int l, const int r, const int rt) {
if (L <= l && r <= R) {
Sum[rt] += C * (r - l + 1);
Add[rt] += C;
return;
}
int m = (l + r) >> 1;
PushDown(rt, m - l + 1, r - m);
if (L <= m)
UpdateSpan(L, R, C, l, m, rt << 1);
if (R > m)
UpdateSpan(L, R, C, m + 1, r, rt << 1 | 1);
PushUp(rt);
}
int Query(const int L, const int R, const int l, const int r, const int rt) { //遍历[L, R]所有结点并返回其和
if (L <= l && r <= R) //[L, R]是要查询的范围,单词查找为恒定值; [l, r]随着搜索的进行而缩小,缩小到是[L, R]的子集即可返回
return Sum[rt];
int m = (l + r) >> 1;
PushDown(rt, m - l + 1, r - m); //下沉更新结点,如果有Add[]的话
int ANS = 0;
if (L <= m)
ANS += Query(L, R, l, m, rt << 1);
if (R > m)
ANS += Query(L, R, m + 1, r, rt << 1 | 1);
return ANS; //返回总和
}
int main(int argc, char** argv) {
cin.tie(0);
ios::sync_with_stdio(false);
int n = 4, L = 1, R = 4, C = 3; //n表示一共几个结点
Build(1, n, 1); //建树
UpdateNode(L, C, 1, n, 1); //修改点(点+C)
UpdateSpan(L, R, C, 1, n, 1); //修改区间(区间所有+C)
int ANS = Query(L, R, 1, n, 1); //求和
cout << ANS << endl;
return 0;
}
[C++]线段树
于 2022-03-17 18:58:43 首次发布
本文介绍了一种使用完全二叉树实现区间维护的方法,包括区间加法操作(如异或、数字和等),通过递归和推导技术优化查找、赋值和查询过程。重点讲解了`Build`、`UpdateNode`、`UpdateSpan`和`Query`函数,以及`PushUp`和`PushDown`策略,适用于实时更新区间内元素的求和问题。
摘要由CSDN通过智能技术生成