题目链接:传送门
思路:构造一棵权值线段树,让其珂持久化(即一棵主席树)。
主席树变量:
int n,m,a[Size]; //题目给出的输入数据
int maxn,b[Size]; //离散化后的数据,maxn表示去重后的数的个数
int tot; //当前主席树内共有多少个节点
int T[Size]; //T[i]表示第i个历史版本的根节点
int ls[Size],rs[Size]; //ls[i],rs[i]分别表示主席树上i节点的左右儿子
int sum[Size]; //表示主席树上i节点的子树中共有多少个树
建树:
int Build(int l,int r) { //建出[l,r]区间
int rt=++tot; //主席树内的节点数++
sum[rt]=0; //一开始树内没有数
if(l<r) {
int mid=(l+r)>>1;
ls[rt]=Build(l,mid);
rs[rt]=Build(mid+1,r);
}
return rt;
}
更新:
int Update(int pre,int l,int r,int v) {
//在前一个历史版本的基础上把v加进去
int rt=++tot;
//rt的左右孩子先指向pre的左右孩子
ls[rt]=ls[pre]; rs[rt]=rs[pre];
//rt的子树内数的数量比pre多1
sum[rt]=sum[pre]+1;
if(l<r) {
int mid=(l+r)>>1;
if(v<=mid) {
ls[rt]=Update(ls[pre],l,mid,v);
} else {
rs[rt]=Update(rs[pre],mid+1,r,v);
}
}
return rt;
}
询问第k小:
int Query(int u,int v,int l,int r,int k) {
//[u,v]表示询问的区间
//[l,r]表示当前区间
if(l==r) return l;
int mid=(l+r)>>1,x=sum[ls[v]]-sum[ls[u]];
//每次在主席树上二分,x表示左子树中有多少个数
if(x>=k) return Query(ls[u],ls[v],l,mid,k);
return Query(rs[u],rs[v],mid+1,r,k-x);
}
完整代码:
#include
#include
#include
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=200005;
const int LOG=20;
int n,m,tot,maxn,a[Size],b[Size],T[Size];
int ls[Size*LOG],rs[Size*LOG],sum[Size*LOG];
int Build(int l,int r) {
int rt=++tot;
sum[rt]=0;
if(l<r) {
int mid=(l+r)>>1;
ls[rt]=Build(l,mid);
rs[rt]=Build(mid+1,r);
}
return rt;
}
int Update(int pre,int l,int r,int v) {
int rt=++tot;
ls[rt]=ls[pre]; rs[rt]=rs[pre];
sum[rt]=sum[pre]+1;
if(l<r) {
int mid=(l+r)>>1;
if(v<=mid) {
ls[rt]=Update(ls[pre],l,mid,v);
} else {
rs[rt]=Update(rs[pre],mid+1,r,v);
}
}
return rt;
}
int Query(int u,int v,int l,int r,int k) {
if(l==r) return l;
int mid=(l+r)>>1,x=sum[ls[v]]-sum[ls[u]];
if(x>=k) return Query(ls[u],ls[v],l,mid,k);
return Query(rs[u],rs[v],mid+1,r,k-x);
}
int main() {
n=read();
m=read();
for(re i=1; i<=n; i++) b[i]=a[i]=read();
sort(b+1,b+1+n);
maxn=unique(b+1,b+1+n)-(b+1);
T[0]=Build(1,maxn);
for(re i=1; i<=n; i++) {
int t=lower_bound(b+1,b+1+maxn,a[i])-b;
T[i]=Update(T[i-1],1,maxn,t);
}
while(m--) {
int l=read();
int r=read();
int k=read();
int id=Query(T[l-1],T[r],1,maxn,k);
printf("%d\n",b[id]);
}
return 0;
}