hdu 4417 Super Mario(主席树+二分)

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:求区间上有多少个数字比h小
思路:利用了主席树求第k小值的作用。如果区间第k小的值是x,那就说明在这个区间上至少有k个数字的值小于等于x。因此我们只要二分这个k值,在满足x<=h时,取那个最大的mid值就行了。

#include  <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5;
int T, n, m, q;
int a[maxn], t[maxn];
struct node {
    int l, r, cnt;
} tree[maxn * 20];
int tot, rt[maxn];
void updata(int l, int r, int pre, int &cur, int x) {
    cur = ++tot;
    tree[cur] = tree[pre];
    tree[cur].cnt++;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(mid >= x) updata(l, mid, tree[pre].l, tree[cur].l, x);
    else updata(mid + 1, r, tree[pre].r, tree[cur].r, x);
}

int query(int l, int r, int pre, int cur, int k) {//求区间第k小
    if(l == r) return l;
    int sum = tree[tree[cur].l].cnt - tree[tree[pre].l].cnt;
    int mid = (l + r) >> 1;
    if(sum >= k) return query(l, mid, tree[pre].l, tree[cur].l, k);
    else return query(mid + 1, r, tree[pre].r, tree[cur].r, k - sum);
}
int main() {
    scanf("%d", &T);
    int cas=1;
    while(T--) {
        tot = 0;
        printf("Case %d:\n",cas++);
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            t[i] = a[i];
        }
        sort(t + 1, t + 1 + n);
        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;
        }
        rt[0] = 0;
        for(int i = 1; i <= n; i++) {
            updata(1, m, rt[i - 1], rt[i], a[i]);
        }
        for(int i = 1; i <= q; i++) {
            int u, v, h;

            scanf("%d%d%d", &u, &v, &h);
            u++;
            v++;
            /*这边加1是因为我们主席树节点的编号是[1,n],
            但是输入的数据是可能从0开始的
            */
            int left = 1;
            int right = v - u + 1;
            int ans = 0;
            while(left <= right) {
                int mid = (left + right) >> 1;
                int num = query(1, m, rt[u - 1], rt[v],mid);
                if(t[num] <= h) {//记得比较的时候要将num离散化回来
                        /*
                        如果区间第mid大的数字是num,
                        那就说明在这个区间里面至少有mid个数字的值小于nuum
                        */
                    if(ans<mid) ans=mid;
                    left=mid+1;
                }
                else right=mid-1;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80710938