主席树

例题:POJ-2104

求区间第k大

sum代表当前数是第几大

对每个数建一棵树

当前树的sum 继承自上一颗树的sum 从祖先到当前数的位置 sum++  

如果前面的数中没有比当前数大的数 sum++后为1 即为第一大的数  

而其它小的数的sum在从祖先到当前数的位置寻找时顺便sum++更新

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cmath>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 1e5 + 6, INF = 0x7fffffff;
int n, m, cnt, root[maxn], a[maxn], x, y, k;
struct node{int l, r, sum;}T[maxn*40];
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)
{
    T[++cnt] = T[y], T[cnt].sum++, x = cnt;
    if(l == r) return;
    int mid = (l + r) / 2;
    if(mid >= pos) return update(l, mid, T[x].l, T[y].l, pos);
    else return 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);
    else 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
", v[query(1, n, root[x-1], root[y], k) - 1]);

    }

    return 0;
}
自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
原文地址:https://www.cnblogs.com/WTSRUVF/p/9672422.html