线段树--segment_tree--lazy_tag

本文详细介绍了 Lazy Tag 的概念及其实现方式,并提供了一段完整的 C++ 代码示例。Lazy Tag 是一种用于线段树区间更新和查询的高效算法,通过延迟更新的方式减少重复计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经过3天的努力,终于解决了lazy_tag的一系列错误,终于可以发篇博客了~
lazy_tag是用来区间查询和修改的一种快速的方法,是线段树一种比较快速的操作。(虽然有些可以用树状数组来解决).
至于它的思想,可以去看刘汝佳的蓝书。

这里只贴代码~

/*
begin,end都是需要查询的区间。
*/
#define lt (node<<1)
#define rt (node<<1|1)
struct segment_tree{
    int tree[N],lazy[N],a[N];

    void create_tree(int node,int l,int r){
        if(l==r){
            tree[node]=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        create_tree(lt,l,mid);
        create_tree(rt,mid+1,r);
        tree[node]=tree[lt]+tree[rt];
        return ;
    }
/*lazy_tag操作,每次查询到那个节点,就要把的信息传给自己的子节点*/
    void down(int node,int l,int r){
        if(!lazy[node]) return;
        int mid=(l+r)>>1;
        tree[lt]+=(mid-l+1)*lazy[node];
        lazy[lt]+=lazy[node];
        tree[rt]+=(r-mid)*lazy[node];
        lazy[rt]+=lazy[node];
        lazy[node]=0;
    }

    int query(int node,int l,int r,int begin,int end){
        if(l>=begin && r<=end){
            return tree[node];
        }
        down(node,l,r);//每次查到这,就得将信息向下传
        int mid=(l+r)>>1,sb=0;
        if(begin<=mid)sb+=query(lt,l,mid,begin,end);
        if(end>mid)sb+=query(rt,mid+1,r,begin,end);
        return sb;
    }

    void update(int node,int l,int r,int begin,int end,int add){
        if(l>=begin && r<=end){
            tree[node]+=(r-l+1)*add;
            lazy[node]+=add;
            return ;
        }
        int mid=(l+r)>>1;
        if(begin<=mid) update(lt,l,mid,begin,end,add);
        if(end>mid) update(rt,mid+1,r,begin,end,add);
        tree[node]+=(min(r,end)-max(l,begin)+1)*add;
    }

}d;

good luck to you!

### 线段树算法与洛谷题解的实现 线段树是一种高效的数据结构,常用于解决区间查询和修改问题。通过懒标记(lazy tag)机制,线段树可以将复杂度优化至 \(O(m \log n)\)[^1]。以下是线段树的基本实现框架及其在洛谷相关题目中的应用。 #### 1. 线段树基本结构 线段树的核心思想是将一个区间划分为多个子区间,并递归地构建一棵二叉树。每个节点表示一个区间,包含该区间的相关信息(如最大值、最小值或区间和)。以下是线段树的基本操作: - **建树**:初始化线段树,递归地将区间划分为左右两部分。 - **单点修改**:更新某个点的值,并将变化传播到所有相关的区间节点。 - **区间查询**:查询某个区间内的信息(如最大值、最小值或区间和)。 - **区间修改**:通过懒标记机制,批量修改某个区间内的值。 ```python class SegmentTree: def __init__(self, arr): self.n = len(arr) self.tree = [0] * (4 * self.n) # 存储区间和 self.lazy = [0] * (4 * self.n) # 懒标记 self.build(0, 0, self.n - 1, arr) def build(self, node, start, end, arr): if start == end: # 叶子节点 self.tree[node] = arr[start] return mid = (start + end) // 2 self.build(2 * node + 1, start, mid, arr) # 左子树 self.build(2 * node + 2, mid + 1, end, arr) # 右子树 self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2] def update_range(self, node, start, end, l, r, value): if self.lazy[node] != 0: # 处理懒标记 self.tree[node] += (end - start + 1) * self.lazy[node] if start != end: self.lazy[2 * node + 1] += self.lazy[node] self.lazy[2 * node + 2] += self.lazy[node] self.lazy[node] = 0 if start > end or start > r or end < l: # 当前区间与目标区间无交集 return if start >= l and end <= r: # 当前区间完全被目标区间覆盖 self.tree[node] += (end - start + 1) * value if start != end: self.lazy[2 * node + 1] += value self.lazy[2 * node + 2] += value return mid = (start + end) // 2 self.update_range(2 * node + 1, start, mid, l, r, value) # 更新左子树 self.update_range(2 * node + 2, mid + 1, end, l, r, value) # 更新右子树 self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2] def query_sum(self, node, start, end, l, r): if start > end or start > r or end < l: # 当前区间与目标区间无交集 return 0 if self.lazy[node] != 0: # 处理懒标记 self.tree[node] += (end - start + 1) * self.lazy[node] if start != end: self.lazy[2 * node + 1] += self.lazy[node] self.lazy[2 * node + 2] += self.lazy[node] self.lazy[node] = 0 if start >= l and end <= r: # 当前区间完全被目标区间覆盖 return self.tree[node] mid = (start + end) // 2 left_sum = self.query_sum(2 * node + 1, start, mid, l, r) # 查询左子树 right_sum = self.query_sum(2 * node + 2, mid + 1, end, l, r) # 查询右子树 return left_sum + right_sum ``` #### 2. 洛谷题解实例 以下为洛谷题目中线段树的应用实例: - **洛谷 P3372 线段树 1**:单点修改,区间查询。 - **洛谷 P3368 树状数组 1**:单点修改,前缀和查询。 - **洛谷 P4588 [TJOI2018]数学计算**:整体查询,无需实现 `query` 函数[^2]。 对于洛谷的线段覆盖问题[^3],可以通过贪心算法解决。首先按照线段的右端点排序,然后依次选择不重叠的线段,确保每次选择的线段结束时间尽可能早,从而最大化后续选择的可能性。 ```cpp #include <bits/stdc++.h> using namespace std; struct Segment { int st, ed; } segs[50005]; bool cmp(Segment a, Segment b) { return a.ed < b.ed; } int main() { int n; cin >> n; for (int i = 0; i < n; ++i) { cin >> segs[i].st >> segs[i].ed; } sort(segs, segs + n, cmp); int cnt = 0, last = -1; for (int i = 0; i < n; ++i) { if (segs[i].st > last) { last = segs[i].ed; cnt++; } } cout << cnt << endl; return 0; } ``` #### 3. 线段重叠问题 对于线段重叠问题[^4],需要找出两条线段的最大重叠长度。可以通过排序和双指针法解决。首先按线段起点排序,然后遍历所有可能的线段对,计算其重叠部分。 ```cpp #include <bits/stdc++.h> using namespace std; struct Segment { int st, ed; } segs[50005]; int main() { int n; cin >> n; for (int i = 0; i < n; ++i) { cin >> segs[i].st >> segs[i].ed; } sort(segs, segs + n, [&](Segment a, Segment b) -> bool { return a.st < b.st; }); int max_overlap = 0; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { int overlap_start = max(segs[i].st, segs[j].st); int overlap_end = min(segs[i].ed, segs[j].ed); if (overlap_start < overlap_end) { max_overlap = max(max_overlap, overlap_end - overlap_start); } } } cout << max_overlap << endl; return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值