模板-线段树

Posted 标题

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板-线段树相关的知识,希望对你有一定的参考价值。

模板

单点修改的线段树

template<typename Avalue, typename Tvalue, // 序列类型,线段树类型
    void (*set)(Tvalue&, Avalue),  // 设置线段树初值
    Tvalue (*op)(Tvalue, Tvalue),    // 线段树合并
    Tvalue (*c)(Tvalue, Tvalue)>    // 线段树修改
struct lazysegment_tree 
    std::vector<Tvalue> tree;
    std::vector<Avalue> array;
    size_t size;

#define LCH id * 2
#define RCH id * 2 + 1

    lazysegment_tree() = default;
    lazysegment_tree(size_t n, std::vector<Avalue>& a)
     : tree((n + 1) << 2), array(a), size(n) 
        build(1, 1, size);
    

    void build(size_t n, std::vector<Avalue>& a) 
        tree.resize((n + 1) << 2);
        size = n;
        array = a;
        build(1, 1, size);
    
    void build(int id, int l, int r) 
        if (l == r) 
            set(tree[id], array[l]);
            return;
        
        int mid = (l + r) >> 1;
        build(LCH, l, mid);
        build(RCH, mid + 1, r);
        tree[id] = op(tree[LCH], tree[RCH]);
    
    void clear() 
        std::vector<Tvalue> tmp1;
        std::vector<Avalue> tmp3;
        tmp1.swap(tree);
        tmp3.swap(array);
        size = 0;
    
    void modify(int id, int l, int r, int pos, Tvalue value) 
        if (pos <= l && r <= pos) 
            tree[id] = c(tree[id], value);
            return;
         
        int mid = (l + r) >> 1;
        if (pos <= mid) modify(LCH, l ,mid, pos, value);
        if (pos > mid) modify(RCH, mid + 1, r, pos, value);
        tree[id] = op(tree[LCH], tree[RCH]);
    
    Tvalue query(int id, int l, int r, int pl, int pr) 
        if (pl <= l && r <= pr) 
            return tree[id];
        
        int mid = (l + r) >> 1;
        if (pr <= mid) return query(LCH, l, mid, pl, pr);
        else if (pl > mid) return query(RCH, mid + 1, r, pl, pr);
        return op(query(LCH, l, mid, pl, pr), query(RCH, mid + 1, r, pl, pr));
    
    template<bool (*cmp)(Tvalue, Tvalue)>
    int lower_bound(int id, int l, int r, int pl, int pr, Tvalue d) 
        if (pl <= l && r <= pr) 
            int mid = (l + r) >> 1;
            if (!cmp(tree[id], d)) return -1;
            else if (l == r) return l;
            else if (cmp(tree[LCH], d)) return lower_bound<cmp>(LCH, l, mid, pl, pr, d);
            else return lower_bound<cmp>(RCH, mid + 1, r, pl, pr, d);
        
        int mid = (l + r) >> 1, lans = -1, rans = -1;
        if (pl <= mid) lans = lower_bound<cmp>(LCH, l, mid, pl, pr, d);
        if (pr > mid) rans = lower_bound<cmp>(RCH, mid + 1, r, pl, pr, d);
        return (lans != -1 ? lans : rans);
    

    void modify(int pos, Tvalue value) 
        modify(1, 1, size, pos, value);
    
    Tvalue query(int pos) 
        return query(1, 1, size, pos, pos);
    
    Tvalue query(int left, int right) 
        return query(1, 1, size, left, right);
    
    template<bool (*cmp)(Tvalue, Tvalue)>
    int lower_bound(int left, int right, Tvalue d) 
        return lower_bound<cmp>(1, 1, size, left, right, d);
    
;

初始化

// 区间最大值, 单点赋值
using ll = long long;
struct tv
    ll max;
;
void set(tv& a, ll b) 
    a =  b ;

tv op(tv a, tv b) 
    return  std::max(a.max, b.max) ;

tv c(tv a, tv b) 
    return b;

bool cmp(tv a, tv b) 
    return a.max >= b.max;

// 或者 lazysegment_tree<ll, tv, set, op, c> seg(n, a);
lazysegment_tree<ll, tv, set, op, c> seg;
seg.build(n, a);

模板第一个参数 Avalue, 为序列 a 的值 类型。
第二个参数 Tvalue, 为 线段树值 的类型。
第三个参数void (*set)(Tvalue&, Avalue), 为线段树 赋初始值 的函数指针。
第四个参数Tvalue (*op)(Tvalue, Tvalue), 为线段树 合并两个段 的函数指针。
第五个参数Tvalue (*c)(Tvalue, Tvalue), 为线段树 单点修改 的函数指针。

可以全局创建,之后通过 build(n, a) 来构造线段树,也可以直接通过构造函数来创建局部变量,参数与 build 相同。

通过 clear 可以清空线段树。

修改

modify(pos, value), value 的类型与 线段树值 一致。

查询

返回值为 线段树的值 类型。

query(left, right), 区间查询。

query(pos), 单点查询。

线段树上二分

lower_bound<cmp>(left, right, d), 查找区间中第一个满足条件的下标,不存在得到 -1

cmp 为函数指针 bool (*cmp)(Tvalue, Tvalue),查找区间中第一个下标 pos 满足 cmp(a[pos], d) == true

区间修改线段树

template<typename Avalue, typename Tvalue, typename Tag, // 序列类型,线段树类型, 标记类型
    void (*set)(Tvalue&, Avalue),  // 设置线段树初值
    Tvalue (*op)(Tvalue, Tvalue),   // 线段树合并
    void (*tag)(Tvalue&, Tag&, Tag), // 下传标记
    Tag (*e)()>    // 清空标记(标记初值)
struct lazysegment_tree 
    std::vector<Tvalue> tree;
    std::vector<Tag> lazy;
    std::vector<Avalue> array;
    size_t size;

#define LCH id * 2
#define RCH id * 2 + 1

    lazysegment_tree() = default;
    lazysegment_tree(size_t n, std::vector<Avalue>& a)
     : tree((n + 1) << 2), lazy((n + 1) << 2, e()), array(a), size(n) 
        build(1, 1, size);
    

    void build(size_t n, std::vector<Avalue>& a) 
        tree.resize((n + 1) << 2);
        lazy.resize((n + 1) << 2, e());
        size = n;
        array = a;
        build(1, 1, size);
    
    void build(int id, int l, int r) 
        if (l == r) 
            set(tree[id], array[l]);
            return;
        
        int mid = (l + r) >> 1;
        build(LCH, l, mid);
        build(RCH, mid + 1, r);
        tree[id] = op(tree[LCH], tree[RCH]);
    
    void clear() 
        std::vector<Tvalue> tmp1;
        std::vector<Tag> tmp2;
        std::vector<Avalue> tmp3;
        tmp1.swap(tree);
        tmp2.swap(lazy);
        tmp3.swap(array);
        size = 0;
    
    void pushdown(int id) 
        if (lazy[id] == e()) return;
        tag(tree[LCH], lazy[LCH], lazy[id]);
        tag(tree[RCH], lazy[RCH], lazy[id]);
        lazy[id] = e();
    
    void modify(int id, int l, int r, int pl, int pr, Tag value) 
        if (pl <= l && r <= pr) 
            tag(tree[id], lazy[id], value);
            return;
         
        pushdown(id);
        int mid = (l + r) >> 1;
        if (pl <= mid) modify(LCH, l ,mid, pl, pr, value);
        if (pr > mid) modify(RCH, mid + 1, r, pl, pr, value);
        tree[id] = op(tree[LCH], tree[RCH]);
    
    Tvalue query(int id, int l, int r, int pl, int pr) 
        if (pl <= l && r <= pr) 
            return tree[id];
        
        pushdown(id);
        int mid = (l + r) >> 1;
        if (pr <= mid) return query(LCH, l, mid, pl, pr);
        else if (pl > mid) return query(RCH, mid + 1, r, pl, pr);
        return op(query(LCH, l, mid, pl, pr), query(RCH, mid + 1, r, pl, pr));
    
    template<bool (*cmp)(Tvalue, Tvalue)>
    int lower_bound(int id, int l, int r, int pl, int pr, Tvalue d) 
        if (pl <= l && r <= pr) 
            int mid = (l + r) >> 1;
            if (!cmp(tree[id], d)) return -1;
            else if (l == r) return l;
            pushdown(id);
            if (cmp(tree[LCH], d)) return lower_bound<cmp>(LCH, l, mid, pl, pr, d);
            else return lower_bound<cmp>(RCH, mid + 1, r, pl, pr, d);
        
        pushdown(id);
        int mid = (l + r) >> 1, lans = -1, rans = -1;
        if (pl <= mid) lans = lower_bound<cmp>(LCH, l, mid, pl, pr, d);
        if (pr > mid) rans = lower_bound<cmp>(RCH, mid + 1, r, pl, pr, d);
        return (lans != -1 ? lans : rans);
    

    void modify(int left, int right, Tag value) 
        modify(1, 1, size, left, right, value);
    
    Tvalue query(int pos) 
        return query(1, 1, size, pos, pos);
    
    Tvalue query(int left, int right) 
        return query(1, 1, size, left, right);
    
    template<bool (*cmp)(Tvalue, Tvalue)>
    int lower_bound(int left, int right, Tvalue d) 
        return lower_bound<cmp>(1, 1, size, left, right, d);
    
;

初始化

// 区间和, 区间加
using ll = long long;
struct tv
    ll sum;
    int size;
;
void set(tv& a, ll b) 
    a =  b, 1 ;

tv op(tv a, tv b) 
    return  a.sum + b.sum, a.size + b.size ;

void tag(tv& a, ll& b, ll c) 
    if (c == 0) return;
    a.sum += c * a.size;
    b += c;

ll e() 
    return 0;

// 或者 lazysegment_tree<ll, tv, ll, set, op, tag, e> seg(n, a);
lazysegment_tree<ll, tv, ll, set, op, tag, e> seg;
seg.build(n, a);

模板第一个参数 Avalue, 为序列 a 的值 类型。
第二个参数 Tvalue, 为 线段树值 的类型。
第三个参数 Tag, 为 标记 的类型。
第四个参数void (*set)(Tvalue&, Avalue), 为线段树 赋初始值 的函数指针。
第五个参数Tvalue (*op)(Tvalue, Tvalue), 为线段树 合并两个段 的函数指针。
第六个参数void (*tag)(Tvalue&, Tag&, Tag), 为线段树 设置标记 的函数指针。
第七个参数Tag (*e)(), 为线段树 标记的初始值 的函数指针。

可以全局创建,之后通过 build(n, a) 来构造线段树,也可以直接通过构造函数来创建局部变量,参数与 build 相同。

通过 clear 可以清空线段树。

修改

modify(left, right, value), value 的类型与 标记 一致。

查询

返回值为 线段树的值 类型。

query(left, right), 区间查询。

query(pos), 单点查询。

线段树上二分

lower_bound<cmp>(left, right, d), 查找区间中第一个满足条件的下标,不存在得到 -1

cmp 为函数指针 bool (*cmp)(Tvalue, Tvalue),查找区间中第一个下标 pos 满足 cmp(a[pos], d) == true

其他

P3372 【模板】线段树 1

OI-wiki

以上是关于模板-线段树的主要内容,如果未能解决你的问题,请参考以下文章

线段树 (区间覆盖模板)

P3373 模板线段树 2 {线段树,模板}

P6012 模板线段树分裂

[模板] 线段树合并

洛谷P3372线段树模板1——线段树

[模板]线段树2-线段树