poj 2104

主席树的模板。

主席树用 n 棵线段树记录 [ 1 , i ] 段数字在区间内出现的个数。

如果数的范围很大,那就先离散化。

首先,每棵线段树有三个元素:左孩子编号,右孩子编号,区间内元素的个数。

设它的元素值为 [ l , r ],mid = ( l + r ) / 2,它的左孩子就记录了 [ l , mid ],右孩子就记录了 [ mid + 1 , r ]。

插入一个元素,如果它大于 mid,那么它就存在于当前线段树的右孩子,否则就插入到左孩子中,直到 l = r。

查找区间第 k 大,就只需要判断它的左孩子中的元素个数是否不大于 k,类似平衡树。

#include"cstdio"
#include"cctype"
#include"algorithm"
using namespace std;
const int maxn=100001,maxo=2000001;
int read()
{
    int c,x=0,s=1; while(!isdigit(c=getchar())) if(c=='-') s=-1;
    while(x=x*10+c-'0',isdigit(c=getchar())); return x*s;
}
struct number
{
    int v,id;
}b[maxn];
int comp(number a,number b)
{
    return a.v<b.v;
}
int cnt,rot,a[maxn],c[maxo][2],s[maxo],p[maxo],map[maxo];
void insert(int &o,int e,int l,int r,int v)
{
    if(o==0) o=++cnt;
    if(l==r)
    {
        s[o]=1;
        return;
    }
    s[o]=s[e]+1;
    int mid=l+r>>1;
    if(v>mid)
    {
        c[o][0]=c[e][0];
        insert(c[o][1],c[e][1],mid+1,r,v);
    }
    else
    {
        c[o][1]=c[e][1];
        insert(c[o][0],c[e][0],l,mid,v);
    }
}
int query(int o,int e,int l,int r,int k)
{
    if(l==r) return l;
    int t=s[c[o][0]]-s[c[e][0]],mid=l+r>>1;
    if(k<=t) return query(c[o][0],c[e][0],l,mid,k);
    else return query(c[o][1],c[e][1],mid+1,r,k-t);
}
int main()
{
    int n=read(),m=read(),w=1;
    for(int i=1;i<=n;i++)
        a[i]=b[i].v=read(),b[i].id=i;
    sort(b+1,b+n+1,comp);
    a[b[1].id]=1;
    map[1]=b[1].v;
    for(int i=2;i<=n;i++)
        if(b[i-1].v==b[i].v) a[b[i].id]=w;
        else
        {
            a[b[i].id]=++w;
            map[w]=b[i].v;
        }
    for(int i=1;i<=n;i++) insert(p[i],p[i-1],1,w,a[i]);
    while(m--)
    {
        int l=read(),r=read(),k=read();
        printf("%d\n",map[query(p[r],p[l-1],1,w,k)]);
    }
    return 0;
}

 

posted @ 2018-02-26 13:57  312432424  阅读(110)  评论(0编辑  收藏  举报