【线段树模板】区间和与区间更新,以洛谷 P3372 【模板】线段树 1为例。

线段树模板:区间和与区间更新

线段树是一种非常强大的数据结构,它允许我们在对数时间内对数组的区间执行查询和更新操作。在这个教程中,我们将通过一个具体的例子——使用线段树来解决洛谷P3372 【模板】线段树 1“区间求和”和“区间更新”的问题来学习线段树的基础模板的应用。

我并不打算动动手指就能把线段树的工作原理描述得非常细致易懂。

如果读者完全不明白线段树,建议在拥有二叉树的前置知识下,对着这里的代码敲一遍,相信你会豁然开朗,自己敲一遍比看别人说一千次还要有效

线段树的基本概念

线段树是一个二叉树,每个节点代表一个区间的聚合信息(例如区间和、最大值或最小值)。对于长度为n的数组,线段树大约需要4 * n的空间来存储(至于为什么是4 * n的空间,请看oi-wiki,这里不多赘述)。

构建线段树

构建线段树涉及将数组分解为多个区间,并将这些区间作为树的叶子节点。内部节点存储子区间的聚合值。构建树的过程是递归的,时间复杂度为O(n)

构建函数

    inline void buildTree(int l, int r, int node_idx = 1) {
   

        tree[node_idx].l = l;
        tree[node_idx].r = r;

        if (l == r) {
   
            tree[node_idx].sum = nums[l];
            return;
        }

        int double_idx = (node_idx << 1);
        int left_chil_idx = double_idx + 1;
        int right_chil_idx = double_idx + 2;

        int mid = l + ((r - l) >> 1);

        buildTree(l, mid, left_chil_idx);
        buildTree(mid + 1, r, right_chil_idx);

        tree[node_idx].sum = tree[left_chil_idx].sum + tree[right_chil_idx].sum;
    }

在这个函数中,我们从根节点开始,递归地将数组分成两部分,直到每个区间只包含一个元素。我们将数组的元素存储在树的叶子节点,然后计算内部节点的值,它是其左右子节点值的和。

懒惰更新

down函数,将之前保存在lazy数组中没有真正进行更新操作的数字,真正执行一次更新操作。

在区间查询和区间更新操作中,如果访问到了某一个节点,并且还要继续深入访问其子节点,就应该将之前保存下来的懒惰更新的值进行真正的更新。

inline void down(int node_idx) {
   

    if (lazy[node_idx] == 0) return;

    int tree_left = tree[node_idx].l;
    int tree_right = tree[node_idx].r;
    int mid = tree_left + ((tree_right - tree_left) >> 1);

    int double_idx = (node_idx << 1);
    int left_chil_idx = double_idx + 1;
    int right_chil_idx = double_idx + 2;

    tree[left_chil_idx].sum += lazy[node_idx] * (mid - tree_left + 1);
    tree[right_chil_idx].sum += lazy[node_idx] * (tree_right - mid);
    lazy[left_chil_idx] += lazy[node_idx];
    lazy[right_chil_idx] += lazy[node_idx];

    lazy[node_idx] = 0;

}

区间查询

区间查询是线段树的核心功能之一。我们可以使用线段树在O(log n)时间内查询任意区间的聚合信息。

查询函数

inline int queryRange(int query_left, int query_right, int node_idx = 1) {
   

    int tree_left = tree[node_idx].l, tree_right = tree[node_idx].r;

    // 完全不与查询目标的区间重合
    if (tree_left > query_right || tree_right < query_left) return 0;
    // 完全与查询目标的区间重合
    if 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值