原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6278
题意:就是给你一个区间,让你求一个最大的h,使得在这个区间里面有h个数大于等于h
思路:二分枚举这个h的值,具体看代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, q, m;
const int maxn = 250005;
int a[maxn], t[maxn], rt[maxn];
// rt是主席树节点
struct node {
int l, r, cnt;
} tree[maxn * 20];
int tot = 0;
void updata(int l, int r, int c, int &cur, int x) {//因为用了cur的引用,所以不需要不build函数
cur = ++tot;
tree[cur] = tree[c];
tree[cur].cnt++;
if(l == r) return ;
int mid = (l + r) >> 1;
if(mid >= x)updata(l, mid, tree[c].l, tree[cur].l, x);
else updata(mid + 1, r, tree[c].r, tree[cur].r, x);
}
int query(int l, int r, int pre, int rt, int k) { //求区间第k大
if(l == r) return l;
int sum = tree[tree[rt].r].cnt - tree[tree[pre].r].cnt;
int mid = (l + r) >> 1;
if(sum >= k) return query(mid + 1, r, tree[pre].r, tree[rt].r, k);
else return query(l, mid, tree[pre].l, tree[rt].l, k - sum);
}
int main() {
while(~scanf("%d%d", &n, &q)) {
tot = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
t[i] = a[i];
}
sort(t + 1, t + 1 + n);
int m = unique(t + 1, t + 1 + n) - t - 1;
for(int i = 1; i <= n; i++) {
a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;
}
for(int i = 1; i <= n; i++) {
updata(1, m, rt[i - 1], rt[i], a[i]);
}
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
int left = 1;
int right = r-l+1;
//求区间第h大
int num = 0;
int ans = 0;
while(left <= right) {
int h = (left + right) >> 1;
num = query(1, m, rt[l - 1], rt[r], h);
//num 表示区间第h大是哪个数
/*
转化思路:只要区间第h大的值大于等于h,说明
在这个区间里至少有h个数的值大于等于h
*/
if(t[num] >= h) {
ans = h;
left = h + 1;
} else {
right = h - 1;
ans = h - 1;
}
}
printf("%d\n", ans);
}
}
return 0;
}