poj 2588

来源:互联网 发布:炒股模拟软件app 编辑:程序博客网 时间:2024/06/05 02:57

题意:给你一个长度为n的数列,以及m个形如l  r  k的询问,即求出区间【l,r】中第k小的值


分析:考虑最简单的想法,可以建立n棵权值线段树来维护,但显然空间会承受不了。这个时候可以发现每一次建立一棵新的线段树,事实上有很多子树是和上一棵树重复的,所以可以直接利用上一棵树的某些节点。每次最多增加logn个节点,空间复杂度最终为nlogn。


技能:空间大小不能开错。


代码:

#include<cstdio>
#include<algorithm>
using namespace std;

#define rep(i,a,b) for (int i=a;i<=b;++i)
#define N 100010

int ls[N*30],rs[N*30],size[N*30],hash[N],root[N],a[N],num[N];
int n,m,sz,tot;

int read()
{
    int f=1,x=0;char ch=getchar();
    for (;ch>'9'||ch<'0';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

void insert(int l,int r,int x,int &y,int k)
{
    size[y=++sz]=size[x]+1;//  size为数的个数
    if (l==r) return;
    int mid=l+r>>1;
    rs[y]=rs[x]; ls[y]=ls[x];
    if (a[k]<=hash[mid]) insert(l,mid,ls[x],ls[y],k);
        else insert(mid+1,r,rs[x],rs[y],k);
}

int query(int l,int r,int x,int y,int k)
{
    if (l==r) return l;
    int mid=l+r>>1;
    if (k<=size[ls[y]]-size[ls[x]]) return query(l,mid,ls[x],ls[y],k);
    else return query(mid+1,r,rs[x],rs[y],k-size[ls[y]]+size[ls[x]]);
}

int main()
{
    n=read(); m=read();
    rep(i,1,n) a[i]=read(),num[i]=a[i];
    sort(num+1,num+n+1);
    hash[++tot]=num[1];
    rep(i,2,n) if (num[i]^num[i-1]) hash[++tot]=num[i];// 离散化
    rep(i,1,n) insert(1,tot,root[i-1],root[i],i);//逐个加入点,并以上一个根结点为基础构建线段树
    rep(i,1,m)
    {
        int u=read(),v=read(),x=read();
        printf("%d\n",hash[query(1,tot,root[u-1],root[v],x)]);
    }
    return 0;
}