首页 > 其他分享 >【复健】线段树

【复健】线段树

时间:2023-08-02 11:57:32浏览次数:32  
标签:复健 lazy ll 线段 mid sum inline void

线段树复健

OJ 上的题还没做完,下午再说(你

概念

一种二叉搜索树,通过二叉树形结构储存数据,能够解决大部分与区间操作有关的问题,当然应用范围不止于区间操作。

原理是用二分(?)维护一定的区间。

主体部分实现

建树

考虑递归建树,一直二分直到只剩一个数据为止。

展开代码
inline void pushup(ll k) {
    sum[k] = sum[ls(k)] + sum[rs(k)];
} //修改和建树都要用到这个操作
inline void build(ll k, ll l, ll r) {
    if(l == r) {sum[k] = a[l]; return; }
    build(ls(k), l, mid(l, r));
    build(rs(k), mid(l, r) + 1, r);
    pushup(k);
}

懒标记(Lazy Tag)

通过标记当前节点左右儿子需要修改的量,减少重复计算,降低时间复杂度。

展开代码
inline void add(ll k, ll l, ll r, ll v) {
    lazy[k] += v, sum[k] += v * len(l, r);
}
inline void pushdown(ll k, ll l, ll r) {
    add(ls(k), l, mid(l, r), lazy[k]);
    add(rs(k), mid(l, r) + 1, r, lazy[k]);
    lazy[k] = 0;
} //下传标记,修改左右儿子的值

区间操作

核心是不断二分并下传标记,当二分到需求的区间内时返回。

区间修改和查询的示例:

展开代码
inline void update(ll k, ll l, ll r, ll x, ll y, ll v) {
    if(l >= x && r <= y) {add(k, l, r, v); return; }
    pushdown(k, l, r);
    if(x <= mid(l, r)) update(ls(k), l, mid(l, r), x, y, v);
    if(mid(l, r) < y) update(rs(k), mid(l, r) + 1, r, x , y, v);
    pushup(k); //更新区间的值
}
inline ll query(ll k, ll l, ll r, ll x, ll y) {
    if(l >= x && r <= y) return sum[k];
    ll ans = 0;
    if(x <= mid(l, r)) ans += query(ls(k), l, mid(l, r), x, y);
    if(mid(l, r) < y) ans += query(rs(k), mid(l, r) + 1, r, x, y);
    return ans;
}

例题

延绵的山峰

没找到原题

展开题面

有一座延绵不断、跌宕起伏的山,最低处海拔为 \(0\), 最高处海拔不超过 \(8848\) 米,从这座山的一端走到另一端的过程中,每走 \(1\) 米海拔就升高或降低 \(1\) 米。有 \(Q\) 个登山队计划在这座山的不同区段登山,当他们攀到各自区段的最高峰时,就会插上队旗。请你写一个程序找出他们插旗的高度。

区间最值,需要注意的是输入数据是编号 \(0\sim n\) 的高度。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
#define mid(l, r) ((l + r) >> 1)
#define len(l, r) (r - l + 1)
#define MyWife Cristallo

using namespace std;

const int N = 5 * 1e6 + 5;

ll n, m, a[N], x, y, z, b, sum[N], lazy[N];

inline void pushup(ll k) {
    sum[k] = max(sum[ls(k)], sum[rs(k)]);
}

inline void build(ll k, ll l, ll r) {
    if(l == r) {sum[k] = a[l]; return; }
    build(ls(k), l, mid(l, r));
    build(rs(k), mid(l, r) + 1, r);
    pushup(k);
}
/*
inline void add(ll k, ll l, ll r, ll v) {
    lazy[k] += v, sum[k] += v * len(l, r); 
}

inline void pushdown(ll k, ll l, ll r) {
    add(ls(k), l, mid(l, r), lazy[k]);
    add(rs(k), mid(l, r) + 1, r, lazy[k]);
    lazy[k] = 0;
}

inline void update(ll k, ll l, ll r, ll x, ll y, ll v) {
    if(l >= x && r <= y) {add(k, l, r, v); return; }
    pushdown(k, l, r);
    if(x <= mid(l, r)) update(ls(k), l, mid(l, r), x, y, v);
    if(mid(l, r) < y) update(rs(k), mid(l, r) + 1, r, x , y, v);
    pushup(k);
}
*/
inline ll query(ll k, ll l, ll r, ll x, ll y) {
    //cout << k << " " << sum[k] << endl;
    if(l >= x && r <= y) return sum[k];
    ll ans = -1;
    //pushdown(k, l, r);
    if(x <= mid(l, r)) ans = max(ans, query(ls(k), l, mid(l, r), x, y));
    if(mid(l, r) < y) ans = max(ans, query(rs(k), mid(l, r) + 1, r, x, y));
    return ans;
}

int main() {
    scanf("%lld", &n);
    scanf("%lld", &a[0]);
    for(ll i = 1; i <= n; ++i) scanf("%lld", a + i);
    build(1, 1, n);
    scanf("%lld", &m);
    while(m--) {
        scanf("%lld%lld", &x, &y);
        //cout << x << " " << y << endl;
        printf("%lld\n", query(1, 1, n, x, y));
    }
    return 0;
}

感谢伟大的 2k3h 先生做出的贡献:

标签:复健,lazy,ll,线段,mid,sum,inline,void
From: https://www.cnblogs.com/Kiichi/p/SegTfujian.html

相关文章

  • [HEOI2013] Segment李超线段树
    RT感觉会模板就差不多了,可用作处理一些线段或直线的问题,转化过来的也可以。比如DP的斜率优化,直线的话只用一个log,线段要两个log。[HEOI2013]Segment模板#include<iostream>usingnamespacestd;constintmod1=39989;constintmod2=1e9;constdoubleesp=1e-9;const......
  • 线段树
    「观前提醒」「文章仅供学习和参考,如有问题请在评论区提出」目录引入基本原理建树区间查询单点修改区间修改+懒惰标记例题P3372【模板】线段树1-洛谷P3373【模板】线段树2-洛谷小结参考资料引入线段树(SegmentTree)是算法竞赛中常用的用来维护区间信息的数据结构......
  • 线段树
    这是洛谷线段树模板题绿标题,线段树好像没什么好总结的,主要看脑子1#include<iostream>2usingnamespacestd;3#defineintlonglong4constintN=1e5+10,inf=0x3f3f3f3f3f3f3f3f;5intn,q,m;6intf[4*N],a[N],lazy1[4*N],lazy2[4*N];7//l......
  • luogu P3733 [HAOI2017] 八纵八横 题解【线段树分治+线性基+可撤销并查集+bitset】
    目录题目大意解题思路code题目大意题目链接给出一张\(n\)个点\(m\)条边的连通无向图,边带边权\(w_i\)。有以下三种操作,共\(q\)次:\(\centerdot\)在点\(x,y\)之间加入一条边权为\(w_i\)的边,如果这是第\(i\)个此种操作,则记这条新边为第\(i\)条。\(\centerdot\)将第\(k......
  • [TJOI2007] 线段
    #[TJOI2007]线段##题目描述在一个$n\timesn$的平面上,在每一行中有一条线段,第$i$行的线段的左端点是$(i,L_{i})$,右端点是$(i,R_{i})$。你从$(1,1)$点出发,要求沿途走过所有的线段,最终到达$(n,n)$点,且所走的路程长度要尽量短。更具体一些说,你在任何时候只能选择向......
  • 复健笔记
    复健笔记P1536把已经联通的块缩成一个,用并查集重编号,然后输出编号数-1即可P1955\(x_1=x_2\)就放在一个联通块内,然后去验证\(x_1\neqx_2\)的都成不成立即可需要把操作离线下来离散化,先加并查集,然后再验连通性P2330最小瓶颈生成树,那直接上kruskal就完事了P1821......
  • lazy 线段树代码
    描述 代码:1classNode{2intl,r;3intsum;4intlazy;5}67classSegmentTree{89privateNode[]tree;1011privateint[]nums;1213publicSegmentTree(int[]nums){14intn=nums.length;15......
  • 懒标记线段树
    1.操作符号含义\(nums\)原数组\(d\)线段树节点维护值\(lazytag\)线段树节点懒标记值\(p\)当前节点\(s\)查询区间的开始\(e\)查询区间的结尾\(l\)节点区间的开始\(r\)节点区间的结尾一般习惯:建树从下标\(1\)开始\(mid=(l+......
  • poj 2886 Who Gets the Most Candies? (线段树单点更新应用)
                           poj2886WhoGetstheMostCandies?DescriptionNchildrenaresittinginacircletoplayagame.Thechildrenarenumberedfrom1toNinclockwiseorder.Eachofthemhasacardwithanon-zerointegeronit......
  • uva 12299 RMQ with Shifts(线段树单点更新初步应用)
                                 uva12299RMQwithShiftsInthetraditionalRMQ(RangeMinimumQuery)problem,wehaveastaticarrayA.Thenforeachquery(L,R)(LR),wereporttheminimumvalueamongA[L],A[L+1],...,A[R].N......