题目传送门
一篇不错的主席树博客:https://blog.csdn.net/ModestCoder_/article/details/90107874
翻译过来的中文题面
问题描述:
给定一个数组 a[1…n],数组元素各不相同,你的程序要对每次查询Q(i,j,k)作出回答,其中Q(i,j,k)的含义为在数组a[i…j]中第k大的数字.
例如,给出数组a=(1, 5, 2, 6, 3, 7, 4).查询语句为Q(2, 5, 3),即从(5,2,6,3)中找出第3大的元素,将之排序得到(2, 3, 5, 6),故第三大的数字是5,所以这次查询的结果应当为5.
输入格式
数据第1行有两个数字N与M,分别表示数组元素的个数与查询次数(1 <= n <= 100000, 1 <= m <= 5000).
第2行有N个数字,表示数组中的各元素,数组中各元素互不相同,并且每个数字的绝对值不超过109.
接下来的m行,每行包含3个数字i,j,k,(1 <= i <= j <= n, 1 <= k <= j - i + 1),表示一次查询Q(i,j,k)
输出格式
对于每次查询,输出查询结果,即a[i…j]中第k大的数字,每个结果占一行
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
Hint
输入数据较多,请使用C语言风格的输入输出(scanf(),printf()),否则可能发生Time Limit Exceed.
这道题就是一道很好主席树入门题,也是最简单的主席树查询区间第K小的用法。刚学主席树,用的还不是很熟,目前也就先敲一个板子记录一下,先入个门,之后慢慢来补充吧。
AC
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
//#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N],lc[N<<5],rc[N<<5],rt[N<<5],sum[N<<5];
int q,js=0,p;
int ans=0;
void build(int &rt,int l,int r)
{
rt=++js;
sum[rt]=0;
if(l==r)return;
int mid=(l+r)>>1;
build(lc[rt],l,mid);
build(rc[rt],mid+1,r);
}
int add(int o,int k,int l,int r)
{
int oo=++js;
lc[oo]=lc[o],rc[oo]=rc[o],sum[oo]=sum[o]+1;
if(l==r)return oo;
int mid=(l+r)>>1;
if(mid>=k)
{
lc[oo]=add(lc[oo],k,l,mid);
}
else
{
rc[oo]=add(rc[oo],k,mid+1,r);
}
return oo;
}
int slary(int x,int y,int l,int r,int k)
{
int mid=(l+r)/2;
ans=sum[lc[y]]-sum[lc[x]];
if(l==r)return l;
if(ans>=k)
{
return slary(lc[x],lc[y],l,mid,k);
}
else
{
return slary(rc[x],rc[y],mid+1,r,k-ans);
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
q=unique(b+1,b+1+n)-b-1;
build(rt[0],1,q);
for(int i=1;i<=n;i++)
{
int k=lower_bound(b+1,b+1+q,a[i])-b;
rt[i]=add(rt[i-1],k,1,q);
}
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[slary(rt[l-1],rt[r],1,q,k)]);
}
return 0;
}