题目:Just h-index
题意
给出n个数,q次询问,每次询问给出一个区间[l,r],要你求出最大的h,使得在[l,r]这个区间内满足,有h个数的值大于等于h。
题解
先建造主席树,每次查询用二分查询,h右边的个数是否大于h,如果是,那么就往右子树走,否则就往左子树走。此题中我们需要处理的是位置,只要第k大的值是大于等于k的,这个k值就可以看成是该区间的一个h,每次区间查询我们只要找到区间最大的h。
正确代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=100005;
int T;
int n,m;
int q;
int tot;
int cnt;
int a[maxn];
int t[maxn];
int b[maxn];
struct node{
int l,r,sum;
}tre[maxn<<5];
int build(int l,int r){
int rt=++cnt;
tre[rt].sum=0;
if(l==r){
return rt;
}
int mid=(l+r)>>1;
tre[rt].l=build(l,mid);
tre[rt].r=build(mid+1,r);
return rt;
}
int update(int t,int l,int r,int k){
int rt=++cnt;
tre[rt].l=tre[t].l;
tre[rt].r=tre[t].r;
tre[rt].sum=tre[t].sum+1;
if(r==l){
return rt;
}
int mid=(l+r)>>1;
if(k<=mid){
tre[rt].l=update(tre[t].l,l,mid,k);
}
else{
tre[rt].r=update(tre[t].r,mid+1,r,k);
}
return rt;
}
int query(int le,int re,int l,int r,int k){
if(l==r){
return l;
}
int x=tre[tre[re].l].sum-tre[tre[le].l].sum;
int mid=(l+r)>>1;
if(x>=k){
return query(tre[le].l,tre[re].l,l,mid,k);
}
else{
return query(tre[le].r,tre[re].r,mid+1,r,k-x);
}
}
int main(){
while(~scanf("%d%d",&n,&q))
{
cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
int m=unique(b+1,b+1+n)-b-1;
sort(b+1,b+1+n);
t[0]=build(1,m);
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+m,a[i])-b;
t[i]=update(t[i-1],1,m,a[i]);
}
int x,y;
while(q--){
scanf("%d%d",&x,&y);
int l=1,r=y-x+1;
int ans=1;
int p;
while(l<=r)
{
int mid=(l+r)>>1;
p=query(t[x-1],t[y],1,m,y-x-mid+2);
//cout<<b[p]<<' '<<mid<<endl;
if(b[p]>=mid)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
}
}
return 0;
}