首页 > 代码库 > 主席树POJ2104

主席树POJ2104

求区间第k大数是多少

用我惯用的线段树写法似乎不适合写主席树,看别人的代码好半天才看懂

用root表示每一个前缀树的根,思想大致是由第i-1个树构成的树建第i个树,由于加入一个数只需要log级别修改,所以建树效率很高。

主席树的精髓在于可持续化,就是说之前区间的信息不去修改它,递推加入新元素的时候,要新开log个空间来存储。因为主席树本身比较占用空间,只需改变这些空间的指向就可以重复利用不变的空间,达到节省空间的目的。

简而言之,主席树是一种重复利用数据不变的空间,建成空间上只有一颗完整的树,但逻辑上是多颗树同时共存的数据结构。

他具有线段树的查询效率。

更大的好处是,在处理某些问题上更符合人类的思维,树的同层之间有很好的性质。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node
{
    int id,val;
}s[100005];
int b[100005],cnt,root[100005];
bool cmp(const node &a,const node &b)
{
    return a.val<b.val;
}
struct node1
{
    int l,r,val;
}tree[100005*20];
int build(int l,int r)
{
    cnt++;
    int now=cnt;
    tree[now].val=0;
    if(l==r)
    return now;
    int mid=(l+r)>>1;
    tree[now].l=build(l,mid);
    tree[now].r=build(mid+1,r);
    return now;
}
void up(int now)
{
    tree[now].val=(tree[tree[now].l].val+tree[tree[now].r].val);
}
int update(int ori,int l,int r,int x)
{
    cnt++;
    int now=cnt;
    tree[now]=tree[ori];
    if(l==r)
    {
        tree[now].val++;
        return now;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
    tree[now].l=update(tree[ori].l,l,mid,x);
    else
    tree[now].r=update(tree[ori].r,mid+1,r,x);
    up(now);
    return now;
}
int query(int l,int r,int ox,int oy,int k)
{
    if(l==r)
    return l;
    int res = tree[tree[oy].l].val- tree[tree[ox].l].val;
    int m = (l+r)/2;
    if(k <= res)
    return query(l,m,tree[ox].l,tree[oy].l,k);
    else
    return query(m+1,r,tree[ox].r,tree[oy].r,k-res);
}
int main()
{
    int n,q;
    while(cin>>n>>q)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&s[i].val);
            s[i].id=i;
        }
        sort(s+1,s+1+n,cmp);
        for(int i=1;i<=n;i++)
        b[s[i].id]=i;
        cnt=0;
        root[0]=build(1,n);
        for(int i=1;i<=n;i++)
        {
            root[i]=update(root[i-1],1,n,b[i]);
        }
        while(q--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            int id=query(1,n,root[a-1],root[b],c);
            printf("%d\n",s[id].val);
        }
    }
    return 0;
}


主席树POJ2104