同一道题开两篇
主席树:
询问[L,R]内第k小:在树R减去树L-1得到的树上二分即可
#include<stdio.h> #include<stdlib.h> int a[100005]={0},b[100005]={0},c[100005]={0},root[100005]={0},ls[2000000]={0},rs[2000000]={0},sum[2000000]={0}; //ls[x]/rs[x]:点x的左/右孩子编号(若a[1]进入树i-1的右孩子,则树i与树i-1左孩子编号相同), sum[x]:出现次数和 int n,sz=0;//sz所有线段树总节点数 void jh(int* a,int* b) { int t=*a; *a=*b; *b=t; } void kp(int low,int high) { int i=low,j=high,mid=a[(i+j)/2]; while(i<j) { while(a[i]<mid) i++; while(a[j]>mid) j--; if(i<=j) { jh(&a[i],&a[j]); jh(&c[i],&c[j]); i++; j--; } } if(j>low) kp(low,j); if(i<high) kp(i,high); } void tj(int left,int right,int x,int& y,int v) { int mid=(left+right)/2; y=++sz;//树i与树i-1在此节点权值不同,此节点需新建 sum[y]=sum[x]+1;//对应区间内某个数出现次数+1,导致该点的权值+1 if(left==right) return; ls[y]=ls[x];//这一步不能省去:递归下一层要么为左,要么为右,不可能两个都走,没走到的那个节点编号y树与x树相同,在这里赋值 rs[y]=rs[x]; if(v<=mid) tj(left,mid,ls[x],ls[y],v); else tj(mid+1,right,rs[x],rs[y],v); } int cx(int l,int r,int k) { int x=root[l-1],y=root[r],left=1,right=n,mid; while(left<right) { mid=(left+right)/2; if(sum[ls[y]]-sum[ls[x]]>=k)//左 { right=mid; x=ls[x]; y=ls[y]; } else//右 { left=mid+1; k-=sum[ls[y]]-sum[ls[x]]; x=rs[x]; y=rs[y]; } } return left; } int main() { int m,i,l,r,k; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); c[i]=i; } kp(1,n); for(i=1;i<=n;i++)//离散化 b[c[i]]=i; for(i=1;i<=n;i++) tj(1,n,root[i-1],root[i],b[i]); for(;m>0;m--) { scanf("%d%d%d",&l,&r,&k); printf("%d\n",a[cx(l,r,k)]); } return 0; }