主席树

//poj 2104
/*
主席树:对于序列的每一个前缀建一棵以序列里的值为下标的线段树(所以要先离散化),
记录该前缀序列里出现的值的次数;
记离散后的标记为1~n; (下面值直接用1~n代替;)
对于区间[x,y]的第k大的值,那么从root[x-1],root[y]开始,
t=root[y].[1,mid]-root[x-1].[1,mid] ,t表示区间[x,y]内值在[1,mid]的个数
先判断t是否大于K,如果t大于k,那么说明在区间[x,y]内存在[1,mid]的数的个数大于k,
也就是第k大的值在[1,mid]中,否则在[mid+1,r]中;

这样我们依次同时从root[x-1],root[y]往下走
当l==r时 第k大的值就是离散后标记为l的值;

如果每棵线段都建完整的化,n*(n<<2)肯定会mle,
我们发现对于前缀[1,i]和前缀[1,i+1]的线段树,如果b[i+1]<=mid (b[i+1]表示a[i+1]离散后的标记)
那么线段树i和线段树i+1的左边是完全相同的,根本不需要在建,只需要用指针指一下就好;
那么对于一棵新的线段树其实我们最多要建的节点数为log(n);nlog(n)的节点数还是可以忍受的;
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define mid (l+r)/2
#define w(i) T[(i)].w
#define ls(i) T[(i)].ls
#define rs(i) T[(i)].rs
using namespace std;
const int N=100000+10;
struct node{
    int ls,rs,w;
    node(){ls=rs=w=0;}
}T[N*20];
int sz,rt[N];
void update(int &i,int l,int r,int x){
    T[++sz]=T[i]; i=sz;
    w(i)++;
    if (l==r) return;
    if (x<=mid) update(ls(i),l,mid,x);
    else update(rs(i),mid+1,r,x);
}
int query(int i,int j,int l,int r,int k){
    if (l==r) return l;
    int t=w(ls(j))-w(ls(i));
    if (t>=k) return query(ls(i),ls(j),l,mid,k);
    else return query(rs(i),rs(j),mid+1,r,k-t);
}

int a[N],b[N],p[N],n,m;
int cmp(int i,int j){
    return a[i]<a[j];
}
int main(){
    int Cas;scanf("%d",&Cas);
    while (Cas--){
        rt[0]=0;
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++){
            scanf("%d",&a[i]);p[i]=i;
        }
        sort(p+1,p+1+n,cmp);
        for (int i=1;i<=n;i++) b[p[i]]=i;//离散化
        sz=0;
        for (int i=1;i<=n;i++){
            rt[i]=rt[i-1];
            update(rt[i],1,n,b[i]);
        }
        for (int i=0;i<m;i++){
            int x,y,k;scanf("%d%d%d",&x,&y,&k);
            int t=query(rt[x-1],rt[y],1,n,k);
            printf("%d\n",a[p[t]]);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值