ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线段树入门

2022-08-10 20:31:43  阅读:125  来源: 互联网

标签:qr return 入门 int 线段 mid seg id


简介

常用来维护区间信息的数据结构,可以在\(Olog(n)\)的时间内实现区间修改,区间信息合并,单点修改。

结构

建树

注意:线段树空间需要开到四倍。

struct Node {
    int minv;
} seg[N * 4];

// 根据左右儿子更新父亲节点信息
void update(int id) {
    seg[id].minv = min(seg[id * 2].minv, seg[id * 2+ 1].minv);
}

// [l, r]
void bulid(int id, int l, int r) {
    if (l == r) {
        seg[id].minv = arr[l];
    } else {
        int mid = (l + r) / 2;
        bulid(id * 2, l, mid);
        bulid(id * 2 + 1, mid + 1, r);
        update(id);
    }
}

多个信息存储和计算

定义一个info操作,简化代码

// 最大子段和 mpre最大前缀 msuf最大后缀 全部
struct info {
    ll mss, mpre, msuf, s;
    // 初始化操作
    info() {}
    info (int a) : mss(a), mpre(a), msuf(a), s(a) {}
};

struct Node {
    info val;
} seg[N * 4];

info operator + (const info &l, const info &r) {
    info res;
    res.mss = max({l.mss, r.mss, l.msuf + r.mpre});
    res.mpre = max(l.mpre, l.s + r.mpre);
    res.msuf = max(r.msuf, r.s + l.msuf);
    res.s = l.s + r.s;
    return res;
}

懒标记

  1. 标记下传

    • 清除标记
  2. 打标记

    • 标记合并
    • 信息更新

修改操作时,标记只到需要修改的节点就停止,等到查询操作需要用到他们的子节点的时候,才会进行下传到下面的子节点。

在打标记的时候考虑标记有无顺序的要求,如何进行标记合并以及打到信息上。

struct info {
    ll maxv;
};

struct tag {
    ll add;
};

struct Node {
    tag t;
    info val;
} seg[N * 4];

info operator + (const info &l, const info &r) {
    return {max(l.maxv, r.maxv)};
}

info operator + (const info &v, const tag &t) {
    return {v.maxv + t.add};
}

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

// 根据左右儿子更新父亲节点信息
void update(int id) {
    seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

//  打标记
void settag(int id, tag t) {
    seg[id].val = seg[id].val + t;
    seg[id].t = seg[id].t + t;
}

// 标记下传
void pushdown(int id) {
    //  标记非空
    if (seg[id].t.add != 0) {
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        seg[id].t.add = 0;
    }
}

// 节点编号 对应区间 修改位置 修改的值
void modify(int id, int l, int r, int ql, int qr, tag t) {
    if (l == ql && r == qr) {
        settag(id, t);
        return;
    } else {
        int mid = (l + r) / 2;
        // 标记下传 !! 
        pushdown(id);
        if (qr <= mid) {
             modify(id * 2, l, mid, ql, qr, t);
        } else if (ql > mid) {
            modify(id * 2 + 1, mid + 1, r, ql, qr, t);
        } else {
            modify(id * 2, l, mid, ql, mid, t);
            modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
        }
        update(id);
    }
}

info query(int id, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) {
        return seg[id].val;
    } else {
        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);
        }
    }
}

单点修改

// 节点编号 对应区间 修改位置 修改的值
void change(int id, int l, int r, int pos, int val) {
    if (l == r) {
        seg[id].minv = 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);
    }
}

区间信息查询

int query(int id, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) {
        return seg[id].minv;
    } else {
        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 min(query(id * 2, l, mid, ql, mid), 
            query(id * 2 + 1, mid + 1, r, mid + 1, qr));
        }
    }
}

线段树上二分

int search(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 {
            int mid = (l + r) / 2;
            if (l == r) {
                return l;
            }
            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, r, d);
            }
        }
    } else {
        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, d);
            if (pos == -1) {
                return search(id * 2 + 1, mid + 1, r, mid + 1, qr, d);
            } else {
                return pos;
            }
        }
    }
}

标签:qr,return,入门,int,线段,mid,seg,id
来源: https://www.cnblogs.com/guyuLihua/p/16573779.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有