静态区间第K小 (主席树模板 洛谷3834 )

#include<bits/stdc++.h>

using namespace std;

const int maxn=2e5+10;

struct Tree{
    int l,r,s;
}tree[maxn*32];//开32倍

struct node{
    int w,pos;
}a[maxn];

int b[maxn],c[maxn],root[maxn];
//b 离散后的位置 c原值 root个树的根

int k=0,tot=0;

void dc(int n)//离散化
{
    b[a[1].pos]=++tot;
    c[tot]=a[1].w;
    for(int i=2;i<=n;i++)
    {
        if(a[i].w!=a[i-1].w)
            tot++;
        b[a[i].pos]=tot;
        c[tot]=a[i].w;
    }
}

int cmp(node a,node b)
{
    return a.w<b.w;
}

int built(int l,int r)//建树 防止爆内存 静态分配空间
{
    int now=++k;
    int mid=(l+r)>>1;
    if(l<r)
    {
        tree[now].l=built(l,mid);
        tree[now].r=built(mid+1,r);
    }
    return now;
}

int updata(int pre,int x,int l,int r)//更新
{
    int mid=(l+r)>>1;
    int now=++k;
    tree[now].s=tree[pre].s+1;//区间内的数的出现次数++
    
    tree[now].l=tree[pre].l;//将左右节点先指向原树
    tree[now].r=tree[pre].r;
    if(l<r)//新建树链
    {
        if(x<=mid)
            tree[now].l=updata(tree[pre].l,x,l,mid);
        else
            tree[now].r=updata(tree[pre].r,x,mid+1,r);
    }
    return now;
}

int query(int u,int v,int x,int l,int r)//查询
{
    int mid=(l+r)>>1;//找到第k小值 
    if(l==r) return l;
    //第r棵线段树左儿子-第(l-1)棵线段树左儿子的值 
    int t=tree[tree[v].l].s-tree[tree[u].l].s;
    //cout<<t<<endl;

    return x<=t?query(tree[u].l,tree[v].l,x,l,mid):query(tree[u].r,tree[v].r,x-t,mid+1,r);
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].w);
        a[i].pos=i;
    }
    sort(a+1,a+1+n,cmp);
    dc(n);

//    for(int i=1;i<=n;i++)
//        cout<<a[i].w<<" ";cout<<endl;
//    for(int i=1;i<=n;i++)
//        cout<<b[i]<<" ";cout<<endl;
//    for(int i=1;i<=n;i++)
//        cout<<c[i]<<" ";cout<<endl;


    root[0]=built(1,tot);

    for(int i=1;i<=n;i++)//建n棵线段树,边加点边建树 
        root[i]=updata(root[i-1],b[i],1,tot);
    for(int i=1;i<=m;i++)
    {
        int l,r,x;
        scanf("%d%d%d",&l,&r,&x);


       // cout<<query(l-1,r,x,1,tot)<<endl;
       //[l,r]就等价于 第r棵线段树-第(l-1)棵线段树 的k小值,返回该节点映射的值 
        printf("%d\n",c[query(root[l-1],root[r],x,1,tot)]);
    }
    return 0;
}

 

posted @ 2019-05-16 21:04  Minun  阅读(282)  评论(0编辑  收藏  举报