【线段树】高度封装的线段树基础模版、高扩展性的线段树打标记以及线段树二分模板----代码源dls的板子

线段树

如果区间信息、标记能够快速合并,可以用线段树

基本储存及实现

储存

封装信息,用info记录存储的信息

后续给val赋值时可使用seg[i].val = {值1,值2…}的形式

typedef long long ll;
const int N = 201000;

struct info {
    int minv;
}
struct node {
    info val;
} seg[N * 4];

重载加号,确定信息修改规则

info operator + (const info &l, const info &r) {
    info a;
    a.minv = min(l. minv, r.minv);
    return a;
}

这样修改的时候只需要在info里增加变量,在重载里增加信息修改规则就可以了

操作

build() ---- 递归建树
//结点是id,区间是[l, r]
void build(int id, int l, int r) {
    if (l == r) {
        seg[id].val = {a[l]};
    } else {
        int mid = (l + r) / 2;
        build(id * 2, l , mid);
        build(id * 2 + 1, mid + 1, r);
        update(id);
    }
}

update() ---- 根据左右儿子更新结点的信息

此处的 + 实现了修改信息的功能

void update(int id) {
    seg[id].minv = seg[id * 2] + seg[id * 2 + 1];
}

写成函数是为了代码更好的扩展

change() ---- 区间修改
//结点是id,区间是[l, r],a[pos]改为val
void change(int id, int l, int r, int pos, int val) {
    if (l == r) {
        seg[id].val = {val};
    } else {
        int mid = (l + r) / 2;
        if (pos <= mid) change(id * 2, l, mid, pos, val);
        else change(id * 2 + 1, mid + 1, r, pos, val);
        update(id);
    }
}
query() ---- 查询
//结点是id,区间是[l, r],查询的区间是[ql, qr]
info query(int id, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) return seg[id].val;
    int mid = (l + r) / 2;
    if (qr <= mid) return query(id * 2, l, mid, ql, qr);
    else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    else {
        return query(id * 2, l, mid, ql, mid) + query(id * 2 + 1, mid + 1, r, mid + 1, qr);
    }
}

例1:线段树1

在这里插入图片描述

只需要修改info:

struct info {
    int minv, mincunt;
}

重载:

info operator + (const info &l, const info &r) {
    info a;
    a.minv = min(l. minv, r.minv);
    //加入修改mincunt的规则
    if (l.minv < r.minv) a.mincunt = l.mincunt;
    else if (l.minv > r.minv) a.mincunt = r.mincunt;
    else a.mincunt = l.mincunt + r.mincunt;
    return a;
}

build和change把递归出口处的赋值改为

seg[id].val = {val, 1};

即可

线段树打标记

存储结构

封装信息,用tag记录标记

手打的话可以不封装info

typedef long long ll;
const int N = 201000;
const ll mod = 1000000007;

struct tag {
    ll mul, add;
}
struct info {
    ll maxv;
}
struct node {
    info val;
    tag t;
    int sz;
}seg[N * 4];

重载:

  1. 信息之间的运算

    info operator + (const info &l, const info &r) {
        return {max(l.maxv, r.maxv)};
    }
    
  2. 信息+标记

    info operator + (const info &v, const tag &t) {
        return {v.maxv * t.mul + t.add};
    }
    
  3. 标记+标记

    注意:t1在t2之前先标记

    tag operator + (const tag &t1, const tag &t2) {
        return {t1.mul * t2.mul % mod, (t1.add * t2.mul + t2.add) % mod};
    }
    

操作

给结点加t标记
void settag(int id, tag t) {
	seg[id].val = (seg[id].val * t.mul + seg[id].sz * t.add) %mod;
    seg[id].t = seg[id].t + t;
]
标记下传
void pushdown(int id) {
	if(seg[id].t.add != 0 || seg[id].t.mul != 1) {
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        seg[id].t.add = 0;
        sge[id].t.mul = 1;
    }
}

query需要在递归之前将标记下传

//结点是id,区间是[l, r],查询的区间是[ql, qr]
info query(int id, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) return seg[id].val;
    int mid = (l + r) / 2;
    
    //加一个标记下传
    pushdown(id);
    
    if (qr <= mid) return query(id * 2, l, mid, ql, qr);
    else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    else {
        return (query(id * 2, l, mid, ql, mid) + query(id * 2 + 1, mid + 1, r, mid + 1, qr)) % mod;
    }
}

区间修改

//结点是id,区间是[l, r],查询的是[ql, qr],标记是t
info modify(int id, int l, int r, int ql, int qr, tag t) {
    if (l == ql && r == qr) {
        settag(id, t);
        return;
    }
    int mid = (l + r) / 2;
    
    //标记下传
    pushdown(id);
    
    if (qr <= mid) modify(id * 2, l, mid, ql, qr);
    else if (ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr);
    else {
        modify(id * 2, l, mid, ql, mid);
        modify(id * 2 + 1, mid + 1, r, mid + 1, qr);
    }
    update(id);
}

注意所有的查询不需要update,单点修改和区间修改都需要update

build

//加上
seg[id].t = {1, 0};
seg[id].sz = r - l + 1;

线段树上二分

例:找出[l, r]中第一个大于d的数字

int serch(int id, int l, int r, int ql, int qr, int d) {
    if(l == ql && r == qr) {
        if(seg[id].val < d) return -1;
        else {
            if(l == r) return l;
            int mid = (l + r) / 2;
        if(seg[id * 2].val >= d) return search(id * 2, l, mid, ql, mid, d);
        else return search(id * 2 + 1, mid + 1, r, mid + 1, qr, d);
        }
    }
   	int mid = (l + r) / 2;
    if(qr <= mid) return search(id * 2, l, mid, ql, qr, d);
    else if(ql > mid) return search(id * 2 + 1, mid + 1, r, ql, qr, d);
    else {
        int pos = search(id * 2, l, mid, ql, mid);
        if(pos == -1) return search(id * 2 + 1, mid + 1, r, mid + 1, qr);
        else return pos;
    }
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵寒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值