题意:给一堆数,找区间第k大的数。
主席树是指在一棵树的基础上复制一棵树,同时又在其中加入一个点。
可以用来查询第k大的点。
这个与直接建立多颗线段树还是有区别的。
这个复制可以节省很多内存~
代码如下:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=100010;
/*
线段树点记录的是数字出现的次数
*/
struct node{
int L,R,sum;
}tree[maxn*20];
struct node1{
int x,id;
bool operator <(node1 y)const{//重载运算符,<时以x的大小来判断。sort时用到
return x<y.x;
}
}a[maxn];
int cnt,n,m;
int root[maxn],rank[maxn];
void create_tree(int num,int &x,int l,int r){//num表示离散化后的编号,x表示根节点,l,r不解释
tree[cnt++]=tree[x],x=cnt-1;//结构体复制,将新建的树复制成上一棵树
/*在树上新增一个节点*/
++tree[x].sum;
if(l==r) return;
int mid=(l+r)>>1;
/*建树的点的位置*/
if(num<=mid) create_tree(num,tree[x].L,l,mid);//左边则向左子树找
else create_tree(num,tree[x].R,mid+1,r);//右边则向右子树
}
int query(int i,int j,int z,int l,int r){
if(l==r) return l;
int t=tree[tree[j].L].sum-tree[tree[i].L].sum;
//先搜索左子树,如果规定区间内的个数比要求的个数多,则在左子树内,反之成立
int mid=(l+r)>>1;
if(z<=t) return query(tree[i].L,tree[j].L,z,l,mid);
else return query(tree[i].R,tree[j].R,z-t,mid+1,r);
}
int main(){
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%d",&a[i].x);
a[i].id=i;
}
sort(a+1,a+1+n);
for(i=1;i<=n;i++) rank[a[i].id]=i;//离散化
cnt=1;
//for(i=1;i<=n;i++) printf("%d ",rank[i]);
for(i=1;i<=n;i++){
root[i]=root[i-1];//将根节点复制
create_tree(rank[i],root[i],1,n);//建树
}
for(i=1;i<=m;i++){
int p,q,z;
scanf("%d%d%d",&p,&q,&z);
printf("%d\n",a[query(root[p-1],root[q],z,1,n)].x); //用根节点来表示树
}
return 0;
}