[C++]线段树

本文介绍了一种使用完全二叉树实现区间维护的方法,包括区间加法操作(如异或、数字和等),通过递归和推导技术优化查找、赋值和查询过程。重点讲解了`Build`、`UpdateNode`、`UpdateSpan`和`Query`函数,以及`PushUp`和`PushDown`策略,适用于实时更新区间内元素的求和问题。
摘要由CSDN通过智能技术生成
/*应用:区间维护(查找、赋值等)
/*目标信息应该满足区间加法(异或、数字之和、最大最小值、最大公因数等等)
*/
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值