吉司机线段树,segment tree beats

吉司机线段树(GSS)是一种基于线段树数据结构的算法,用于解决区间查询问题。它被广泛应用于解决数组的区间最大子段和、区间最小子段和以及区间和等问题。

线段树是一种二叉树,每个节点表示一个区间。根节点表示整个数组的区间,而每个叶子节点表示数组的一个单元。每个节点维护一个区间的信息,如区间和、区间最大值、区间最小值等。

吉司机线段树的特点是通过节点间的合并操作来维护区间信息。具体来说,每个节点的信息是通过合并其左子节点和右子节点的信息得到的。例如,对于求区间和的问题,每个节点的信息就是其左子节点的和加上右子节点的和。

为了更高效地进行区间查询,吉司机线段树还引入了一些优化措施,如延迟更新和懒惰标记。延迟更新可以在需要时才更新节点的信息,而懒惰标记可以将更新操作推迟到需要时才进行。

吉司机线段树的构建时间复杂度为O(nlogn),其中n是数组的长度。区间查询的时间复杂度为O(logn)。由于吉司机线段树的性能优秀,它被广泛应用于解决各种区间查询问题,如最大子段和、最小子段和、区间和等。

以下粘上代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int INF = 1e9; // 定义一个无穷大的数

struct Node {
    int mx1, mx2, cnt_mx1; // 最大值,次大值,最大值的个数
    int lazy_tag; // 懒惰标记

    Node() : mx1(-INF), mx2(-INF), cnt_mx1(0), lazy_tag(0) {}
};

class SegmentTree {
private:
    vector<Node> tree;
    vector<int> arr;

    void push_up(int idx) {
        tree[idx].mx1 = max(tree[idx * 2].mx1, tree[idx * 2 + 1].mx1);
        tree[idx].mx2 = max(min(tree[idx * 2].mx1, tree[idx * 2 + 1].mx1), 
                            max(tree[idx * 2].mx2, tree[idx * 2 + 1].mx2));
        tree[idx].cnt_mx1 = 0;
        if (tree[idx * 2].mx1 == tree[idx].mx1) tree[idx].cnt_mx1 += tree[idx * 2].cnt_mx1;
        if (tree[idx * 2 + 1].mx1 == tree[idx].mx1) tree[idx].cnt_mx1 += tree[idx * 2 + 1].cnt_mx1;
    }

    void push_down(int idx) {
        if (tree[idx].lazy_tag != 0) {
            tree[idx * 2].mx1 += tree[idx].lazy_tag;
            tree[idx * 2 + 1].mx1 += tree[idx].lazy_tag;
            tree[idx * 2].lazy_tag += tree[idx].lazy_tag;
            tree[idx * 2 + 1].lazy_tag += tree[idx].lazy_tag;
            tree[idx].lazy_tag = 0;
        }
    }

public:
    SegmentTree(const vector<int>& nums) {
        int n = nums.size();
        arr = nums;
        tree.resize(n * 4);
        build(1, 0, n - 1);
    }

    void build(int idx, int l, int r) {
        if (l == r) {
            tree[idx].mx1 = arr[l];
            tree[idx].cnt_mx1 = 1;
            return;
        }
        int mid = (l + r) / 2;
        build(idx * 2, l, mid);
        build(idx * 2 + 1, mid + 1, r);
        push_up(idx);
    }

    void update(int idx, int l, int r, int L, int R, int val) {
        if (L <= l && r <= R) {
            if (val >= tree[idx].mx1) return;
            if (val > tree[idx].mx2) {
                tree[idx].mx1 = val;
                tree[idx].lazy_tag = val;
                return;
            }
        }
        push_down(idx);
        int mid = (l + r) / 2;
        if (L <= mid) update(idx * 2, l, mid, L, R, val);
        if (R > mid) update(idx * 2 + 1, mid + 1, r, L, R, val);
        push_up(idx);
    }

    int query(int idx, int l, int r, int L, int R) {
        if (L <= l && r <= R) return tree[idx].mx1;
        push_down(idx);
        int ans = -INF;
        int mid = (l + r) / 2;
        if (L <= mid) ans = max(ans, query(idx * 2, l, mid, L, R));
        if (R > mid) ans = max(ans, query(idx * 2 + 1, mid + 1, r, L, R));
        return ans;
    }
};

int main() {
    vector<int> nums = {3, 2, 4, 5, 1, 2}
    SegmentTree st(nums);

    cout << st.query(1, 0, nums.size() - 1, 0, 3) << endl; // 查询区间[0, 3]的最大值
    st.update(1, 0, nums.size() - 1, 0, 2, );

    return 0;

}

在讨论线段树的关键操作之前,我们先来了解一下线段树的基本概念和性质。线段树是一种用于解决区间查询问题的数据结构,它将一个区间划分为多个子区间,并对每个子区间维护一些有用的信息,例如最大值、最小值、和等等。线段树的构建和查询操作都可以在对数时间复杂度内完成。需要注意以下几点:

  1. 线段树的构建:构建线段树时,需要考虑边界条件。通常情况下,线段树的叶子节点对应于原始输入数组的元素,因此线段树的构建过程涉及到对叶子节点的赋值。同时,我们还需要确定线段树的递归终止条件,即当区间缩小到只包含一个元素时,不再继续划分区间。

  2. 线段树的查询操作:线段树的查询操作主要是通过搜索适当的区间来获得所需的信息。在进行查询时,需要考虑到以下几个问题:a) 判断当前搜索的区间是否和目标区间有交集,如果没有交集,则可以直接返回空值;b) 如果当前搜索的区间完全包含在目标区间内,那么可以直接返回当前区间的信息;c) 如果当前搜索的区间和目标区间部分重叠,那么需要对当前区间进行进一步划分并递归搜索。

  3. 线段树的更新操作:线段树的更新操作主要包括对叶子节点的更新以及对父节点的更新。当某个叶子节点的值发生变化时,我们需要更新对应的父节点的信息。这可以通过递归地向上更新实现。

  4. 线段树的空间复杂度:线段树的空间复杂度取决于构建的树的高度。在一般情况下,线段树的空间复杂度为O(NlogN),其中N是原始输入的大小。但是,通过使用压缩技术(如吉司机线段树)可以将空间复杂度降低到O(N)。

在实际应用中,还有许多其他的细节需要注意,如线段树的边界条件处理、懒惰更新、区间合并等。因此,在使用吉司机线段树时,我们需要仔细考虑这些细节,以确保正确性和效率。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值