题目链接:Just h-index.
大致题意
给你一个长度为n的序列,有m个询问。
每次询问 [L,R]区间中满足:区间中第k大的数大于等于k,求出最大的那个k。
思路
要求区间第k大的数,我们想到要用主席树。
由于k值单调有界,故求最大的k可用二分答案解决。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define N 200005
using namespace std;
int a[N],b[N],n,m,p,q,sz;//结点个数
int ls[N<<5],rs[N<<5],sum[N<<5],rt[N<<5];
inline int read()
{
char ch=getchar();
int flag=1,ans=0;
while(ch<'0'||ch>'9')
{
if(ch=='-')
flag=-1;
ch=getchar();
}
while(ch>='0' && ch<= '9')
{
ans=ans*10+ch-'0';
ch=getchar();
}
return ans*flag;
}
void build(int l,int r,int &rt)
{
rt = ++sz, sum[rt] = 0;
if(l == r) return;
int mid = (l+r) >> 1;
build(l,mid,ls[rt]);
build(mid+1,r,rs[rt]);
}
int update(int l,int r,int o)
{
int oo = ++sz;
ls[oo] = ls[o], rs[oo] = rs[o], sum[oo] = sum[o] + 1;
if(l == r) return oo;
int mid = (l + r) >> 1;
if(p <= mid)
ls[oo] = update(l,mid,ls[oo]);
else
rs[oo] = update(mid+1,r,rs[oo]);
return oo;
}
int query(int u,int v,int l,int r,int k)
{
int mid = (l + r) >> 1;
int x = sum[rs[v]] - sum[rs[u]];
if(l == r) return l;
if(x >= k) return query(rs[u],rs[v],mid+1,r,k);
else return query(ls[u],ls[v],l,mid,k-x);
}
void init()
{
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
memset(sum,0,sizeof(sum));
memset(rt,0,sizeof(rt));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=1; i<=n; i++)
{
a[i] = read();
b[i] = a[i];
}
sort(b+1,b+1+n);
q = unique(b+1,b+1+n) - b - 1;
build(1,q,rt[0]);
for(int i=1; i<=n ;i++)
{
p = lower_bound(b+1,b+1+q,a[i]) - b;
rt[i] = update(1,q,rt[i-1]);
}
while(m--)
{
int l=read(), r=read();
int l1 = 1,r1 = r-l+1; // k的范围
int ans=1;
while(l1 <= r1)
{
int mid = (l1+r1) >> 1;
int h = b[query(rt[l-1],rt[r],1,q,mid)];
if(h >= mid)
{
ans = mid;
l1 = mid + 1;
}
else
{
r1 = mid - 1;
}
}
printf("%d\n",ans);
}
}
return 0;
}