Template. Persistent Segment Tree

2018-08-20

可持久化線段樹適用於查詢不同版本的線段樹,其運作方式非常簡單,就是將要更改的點先複製一遍,未更改的點則不動。而且每新增一個節點,就建立一個新的root,所以我們就可以透過不同的root來讀取不同版本的值。

如下圖:

以下為區間和的持久化線段樹,同時也為Zero Judge a331的AC Code。

#include <bits/stdc++.h>
using namespace std;

struct node {
    int sum;
    node *l, *r;
    node(){l = r = nullptr;}
    node(int n) : sum(n){l = r = nullptr;}
    void up() {
        sum = 0;
        if (l)
            sum += l->sum;
        if (r)
            sum += r->sum;
    }
}*tree[100010];
int in[100010];
int n, m;
node* build(int l, int r) {
    node *o = new node(0);
    if (l == r)
        return o;
    int m = (l + r) >> 1;
    o->l = build(l, m);
    o->r = build(m + 1, r);
    return o;
}
node* insert(node *o, int l, int r, int x, int num) {
    if (l > x || x > r)
        return o;
    o = new node(*o);
    if (l == r && r == x) {
        o->sum = num;
        return o;
    }
    int m = (l + r) >> 1;
    o->l = insert(o->l, l, m, x, num);
    o->r = insert(o->r, m + 1, r, x, num);
    o->up();
    return o;
}
int find(node *f, node *b, int l, int r, int k) {
    if (l == r)
        return l;
    int m = (l + r) >> 1;
    int nxt = b->l->sum - f->l->sum;
    if (k <= nxt)
        return find(f->l, b->l, l, m, k);
    return find(f->r, b->r, m + 1, r, k - nxt);
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    while (cin >> n >> m) {
        vector<int> v;
        for (int i = 1; i <= n; i++) {
            cin >> in[i];
            v.emplace_back(in[i]);
        }
        sort(v.begin(), v.end());
        v.resize(unique(v.begin(), v.end()) - v.begin());
        tree[0] = build(0, v.size());
        for (int i = 1; i <= n; i++)
            tree[i] = insert(tree[i - 1], 0, v.size(), lower_bound(v.begin(), v.end(), in[i]) - v.begin(), 1);
        while (m--) {
            int a, b, c;
            cin >> a >> b >> c;
            cout << v[find(tree[a - 1], tree[b], 0, v.size(), c)] << '\n';
        }
    }
    return 0;
}