一维动态前缀和(区修区查)

一维动态前缀和(区修区查)

线段树(Segment Tree)是一种支持区间修改和区间查询的数据结构。
它能在 的时间复杂度内完成区间操作,比树状数组更加灵活,是实现区间修改的理想选择。

基本概念

线段树的核心思想是:

  1. 将区间划分成多个子区间
  2. 每个节点维护一个区间的信息
  3. 使用懒标记处理区间修改
  4. 支持多种区间操作(和、最大值、最小值等)

实现方式

线段树的关键操作:

  1. build:构建线段树
  2. pushDown:下传懒标记
  3. update:区间修改
  4. query:区间查询

基本实现

class SegmentTree {
private:
    vector<int> tree, lazy;
    int n;
    
    void pushDown(int node, int start, int end) {
        if (lazy[node] != 0) {
            int mid = (start + end) >> 1;
            int leftNode = node * 2 + 1;
            int rightNode = node * 2 + 2;
            
            // 更新左子树
            tree[leftNode] += lazy[node] * (mid - start + 1);
            lazy[leftNode] += lazy[node];
            
            // 更新右子树
            tree[rightNode] += lazy[node] * (end - mid);
            lazy[rightNode] += lazy[node];
            
            lazy[node] = 0;
        }
    }

public:
    SegmentTree(int size) : n(size) {
        tree.resize(4 * n);
        lazy.resize(4 * n);
    }
    
    void update(int node, int start, int end, int left, int right, int val) {
        if (left > end || right < start) return;
        
        if (left <= start && end <= right) {
            tree[node] += val * (end - start + 1);
            lazy[node] += val;
            return;
        }
        
        pushDown(node, start, end);
        int mid = (start + end) >> 1;
        update(node * 2 + 1, start, mid, left, right, val);
        update(node * 2 + 2, mid + 1, end, left, right, val);
        tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2];
    }
    
    int query(int node, int start, int end, int left, int right) {
        if (left > end || right < start) return 0;
        
        if (left <= start && end <= right) {
            return tree[node];
        }
        
        pushDown(node, start, end);
        int mid = (start + end) >> 1;
        return query(node * 2 + 1, start, mid, left, right) +
               query(node * 2 + 2, mid + 1, end, left, right);
    }
};
class SegmentTree {
    private int[] tree;
    private int[] lazy;
    private int n;
    
    public SegmentTree(int size) {
        n = size;
        tree = new int[4 * n];
        lazy = new int[4 * n];
    }
    
    private void pushDown(int node, int start, int end) {
        if (lazy[node] != 0) {
            int mid = (start + end) >> 1;
            int leftNode = node * 2 + 1;
            int rightNode = node * 2 + 2;
            
            // 更新左子树
            tree[leftNode] += lazy[node] * (mid - start + 1);
            lazy[leftNode] += lazy[node];
            
            // 更新右子树
            tree[rightNode] += lazy[node] * (end - mid);
            lazy[rightNode] += lazy[node];
            
            lazy[node] = 0;
        }
    }
    
    public void update(int node, int start, int end, int left, int right, int val) {
        if (left > end || right < start) return;
        
        if (left <= start && end <= right) {
            tree[node] += val * (end - start + 1);
            lazy[node] += val;
            return;
        }
        
        pushDown(node, start, end);
        int mid = (start + end) >> 1;
        update(node * 2 + 1, start, mid, left, right, val);
        update(node * 2 + 2, mid + 1, end, left, right, val);
        tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2];
    }
    
    public int query(int node, int start, int end, int left, int right) {
        if (left > end || right < start) return 0;
        
        if (left <= start && end <= right) {
            return tree[node];
        }
        
        pushDown(node, start, end);
        int mid = (start + end) >> 1;
        return query(node * 2 + 1, start, mid, left, right) +
               query(node * 2 + 2, mid + 1, end, left, right);
    }
}
class SegmentTree:
    def __init__(self, size):
        self.n = size
        self.tree = [0] * (4 * size)
        self.lazy = [0] * (4 * size)
    
    def push_down(self, node, start, end):
        if self.lazy[node] != 0:
            mid = (start + end) >> 1
            left_node = node * 2 + 1
            right_node = node * 2 + 2
            
            # 更新左子树
            self.tree[left_node] += self.lazy[node] * (mid - start + 1)
            self.lazy[left_node] += self.lazy[node]
            
            # 更新右子树
            self.tree[right_node] += self.lazy[node] * (end - mid)
            self.lazy[right_node] += self.lazy[node]
            
            self.lazy[node] = 0
    
    def update(self, node, start, end, left, right, val):
        if left > end or right < start:
            return
        
        if left <= start and end <= right:
            self.tree[node] += val * (end - start + 1)
            self.lazy[node] += val
            return
        
        self.push_down(node, start, end)
        mid = (start + end) >> 1
        self.update(node * 2 + 1, start, mid, left, right, val)
        self.update(node * 2 + 2, mid + 1, end, left, right, val)
        self.tree[node] = self.tree[node * 2 + 1] + self.tree[node * 2 + 2]
    
    def query(self, node, start, end, left, right):
        if left > end or right < start:
            return 0
        
        if left <= start and end <= right:
            return self.tree[node]
        
        self.push_down(node, start, end)
        mid = (start + end) >> 1
        return (self.query(node * 2 + 1, start, mid, left, right) +
                self.query(node * 2 + 2, mid + 1, end, left, right))

应用场景

线段树在很多问题中都有应用:

  1. 区间修改和查询
  2. 区间最值查询
  3. 区间统计问题
  4. 动态维护区间信息
  5. 扫描线算法

示例:区间加法

class Solution {
    SegmentTree* st;
public:
    vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
        st = new SegmentTree(length);
        
        // 执行所有更新操作
        for (const auto& update : updates) {
            int start = update[0];
            int end = update[1];
            int val = update[2];
            st->update(0, 0, length-1, start, end, val);
        }
        
        // 查询每个位置的值
        vector<int> result(length);
        for (int i = 0; i < length; i++) {
            result[i] = st->query(0, 0, length-1, i, i);
        }
        
        return result;
    }
};
class Solution {
    private SegmentTree st;
    
    public int[] getModifiedArray(int length, int[][] updates) {
        st = new SegmentTree(length);
        
        // 执行所有更新操作
        for (int[] update : updates) {
            int start = update[0];
            int end = update[1];
            int val = update[2];
            st.update(0, 0, length-1, start, end, val);
        }
        
        // 查询每个位置的值
        int[] result = new int[length];
        for (int i = 0; i < length; i++) {
            result[i] = st.query(0, 0, length-1, i, i);
        }
        
        return result;
    }
}
class Solution:
    def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:
        st = SegmentTree(length)
        
        # 执行所有更新操作
        for start, end, val in updates:
            st.update(0, 0, length-1, start, end, val)
        
        # 查询每个位置的值
        result = []
        for i in range(length):
            result.append(st.query(0, 0, length-1, i, i))
        
        return result

时间复杂度

  • 初始化:
  • 区间修改:
  • 区间查询:
  • 空间复杂度:

线段树 vs 树状数组

优点:

  1. 支持区间修改
  2. 更加灵活
  3. 可以维护多种信息

缺点:

  1. 实现复杂
  2. 代码量大
  3. 常数较大

注意事项

  1. 正确处理懒标记
  2. 注意区间边界
  3. 合理分配数组大小
  4. 处理整数溢出

练习建议

  1. 实现基本的线段树操作
  2. 尝试不同的区间操作
  3. 解决区间统计问题
  4. 实现其他线段树变体
牛客代码笔记-牛栋 文章被收录于专栏

汗牛充栋,学海无涯。<br/> 内含算法知识点讲解,以及牛客题库精选例题。<br/> 学习算法,从牛栋开始。

全部评论

相关推荐

用微笑面对困难:你出于礼貌叫了人一声大姐,大姐很欣慰,她真把你当老弟
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务