可持久化线段树

Posted tyner

tags:

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

特点

老师说,可持久化线段树一个重要的特点就是,它的询问都是单点询问...
先记这....等我做多了题目之后再补充

李超线段树

用于维护若干个一次函数的最值

核心思想就是标记永久化, 线段树每个节点维护在该区间中点取值最大的线段,查询时求一条从上到下的链上log个线段的最值。
————一位大佬FlashHu

例题

P4097 [HEOI2013]Segment

思路: 一个区间只有在绝对不可能是答案的时候才更新,否则就把原来的/插入的的区间往下递归,插入的/原来的区间就放在当前区间,这样,我们只要把合适的区间往下递归,合适的区间留下,那么对于当前区间,无论是左边还是右边,我们都能求出最优解

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX = 40000;

int n, lastans, cnt;

struct line{
    double k, b;
    int id;
    line()  {}
    line(int x1, int y1, int x2, int y2, int cnt) : id(cnt) {
        if(x1 == x2) k = 0, b = max(y1, y2);
        else k = (double)(y2-y1)/(x2-x1), b = (double)y1-k*x1;
    }
    double calc(int x) {
        return k*x+b;
    }
}tr[MAX<<2];

bool cmp(line a, line b, int x) {//在点x处b高于a || 高度相等且b的id比a小 ->返回1 
    double tmp1 = a.calc(x), tmp2 = b.calc(x);
    return tmp1 != tmp2 ? tmp1 < tmp2 : a.id > b.id;
}
line query(int o, int l, int r, int x) {//因为是要返回id,而不是高度,所以返回类型设为line好一些 
    if(l == r) return tr[o];
    int mid = (l+r)>>1;
    line tmp;
    if(x <= mid) tmp = query(o<<1, l, mid, x);
    else tmp = query(o<<1|1, mid+1, r, x);
    return cmp(tr[o], tmp, x) ? tmp : tr[o];
}
void solve(int o, int l, int r, line x) { //建议手画
    if(!tr[o].id) {tr[o] = x; return ;}
    if(cmp(tr[o], x, l)) swap(tr[o], x);//目前tr[o]里保存的是左端点高的
    if(l == r || tr[o].k == x.k) return ;//是叶子节点 或 是两条平行直线 的情况上一行考虑过了 
    int mid = (l+r)>>1;
    double delta = (tr[o].b-x.b)/(x.k-tr[o].k);//计算交点
    if(delta < l || delta > r) return ;//交点不在区间内 ->也是在cmp那行考虑过了
    //我们要下传的,肯定是可能成为答案的,即tr[o]或x 的 [l, delta]或[delta, r],其它部分就不用了 
    if(delta <= mid) solve(o<<1, l, mid, tr[o]), tr[o] = x;
    //交点在左区间->tr[o]的delta以左可能是答案,并且我们又要保证整个区间中delta以右的答案不丢,所以tr[o] = x 
    else solve(o<<1|1, mid+1, r, x);//交点在右区间->x的delta以右可能是答案 
}

void change(int o, int l, int r, int ql, int qr, line tmp) {
     if(ql <= l && r <= qr) {solve(o, l, r, tmp); return ;}
     int mid = (l+r)>>1;
     if(ql <= mid) change(o<<1, l, mid, ql, qr, tmp);
     if(mid < qr) change(o<<1|1, mid+1, r, ql, qr, tmp);
}

int main() {
    scanf("%d",&n);
    int cmd, x1, x2, y1, y2;   
    for(int i = 1; i <= n; i++) {
        scanf("%d",&cmd);
        if(cmd == 1) {
            scanf("%d%d%d%d",&x1, &y1, &x2, &y2);
            x1 = (x1+lastans-1)%39989+1, y1 = (y1+lastans-1)%(1000000000)+1;
            x2 = (x2+lastans-1)%39989+1, y2 = (y2+lastans-1)%(1000000000)+1;
            if(x1 > x2) swap(x1, x2), swap(y1, y2);
            change(1, 1, MAX, x1, x2, line(x1, y1, x2, y2, ++cnt));
        } else {
            scanf("%d",&x1);
            x1 = (x1+lastans-1)%39989+1;
            lastans = query(1, 1, MAX, x1).id;
            printf("%d
", lastans);
        }
    }
}

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

P3834 模板可持久化线段树 1(主席树)

洛谷 P3835 模板可持久化平衡树

重码数据结构主席树(可持久化线段树)

二打可持久化线段树感想

YSZOJ:#247. [福利]可持久化线段树 (最适合可持久化线段树入门)

可持久化