主席树模板(poj2104)

 

主席树是可持久化线段树,可以记录线段树的历史版本。

 

代码中和线段树不同的是,l,r记录的是左右子树编号,因为普通的线段树版本中,左右子树自然就是o<<1和o<<1|1,但是主席树中并不保证这个特性,所以需要记录一下。

 

代码是

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 1e5+7;
int n, m, cnt, root[N], a[N], x, y, k;
// 主席树解决区间第k大的问题
// poj-2104
struct node { int l, r, sum; } T[N*40]; // 左儿子右儿子编号 区间和 T[i]维护前缀1~i
vector<int> v;
int getid(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; }

void update(int l, int r, int &x, int y, int pos) { // sum记录的是l和r之间的数
    T[++cnt] = T[y], T[cnt].sum++, x = cnt;
    if (l == r) return ;
    int mid = (l+r)>>1;
    if (mid >= pos) update(l, mid, T[x].l, T[y].l, pos);
    else update(mid+1, r, T[x].r, T[y].r, pos);
}

int query(int l, int r, int x, int y, int k) {
    if (l == r) return l;
    int mid = (l+r) / 2;
    int sum = T[T[y].l].sum - T[T[x].l].sum;
    if (sum >= k) return query(l, mid, T[x].l, T[y].l, k);
    return query(mid+1, r, T[x].r, T[y].r, k - sum);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), v.push_back(a[i]);
    sort(v.begin(), v.end()), v.erase(unique(v.begin(), v.end()), v.end());
    for (int i = 1; i <= n; ++i) update(1, n, root[i], root[i-1], getid(a[i]));
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d%d", &x, &y, &k);
        printf("%d\n", v[query(1,n,root[x-1],root[y],k)-1]);
    }
    return 0;
}

 

posted @ 2016-10-04 21:11  我不吃饼干呀  阅读(836)  评论(0编辑  收藏  举报