You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##P1801 黑匣子

虽说是堆题,但也可以用主席树不是? 对于每个要get的地方,相当于询问区间为[1,x],其实就是模板题啦

#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 200010;

//快读
int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
int n, m;
int a[N], b[N], bl; // b和bl是一组用于离散化的数组,bl为b的数组中有用数字的个数一般下标0不放东西

struct Node {
    int l, r, cnt;
} tr[N << 5];
int root[N], idx;

//用于离散化的二分查找
int find(int x) {
    return lower_bound(b + 1, b + 1 + bl, x) - b;
}

//经典的主席树插入
void insert(int &u, int l, int r, int x) {
    tr[++idx] = tr[u]; //新开一个节点idx++,将新节点指向旧的tr[u]
    tr[idx].cnt++;     //新节点的cnt因为多插入了一个数字所以个数+1,这样处理的话省去了pushup
    u = idx;           //因为是地址引用需要回写u等于idx

    if (l == r) return; //如果已经到了叶子节点,上面的操作就足够了,可以直接返回,否则需要继续向下递归

    int mid = (l + r) >> 1;
    if (x <= mid)
        insert(tr[u].l, l, mid, x); //因为tr[u]进入本函数时最先把旧的复制过来所以tr[u].l也是上一个版本的左儿子节点
    else
        insert(tr[u].r, mid + 1, r, x);
}
// p:前面的版本q:后面的版本,[l,r]:控制的范围
// k:要查找第k小的数字
int query(int p, int q, int l, int r, int k) {
    if (l == r) return l;
    int mid = (l + r) >> 1;
    int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
    if (k <= cnt)
        return query(tr[p].l, tr[q].l, l, mid, k);
    else
        return query(tr[p].r, tr[q].r, mid + 1, r, k - cnt);
}

int main() {
    n = read(), m = read();
    for (int i = 1; i <= n; i++) a[i] = b[i] = read();

    sort(b + 1, b + 1 + n);
    bl = unique(b + 1, b + 1 + n) - b - 1;

    for (int i = 1; i <= n; i++) {
        root[i] = root[i - 1];              //开新版本号i,抄袭上一个版本i-1的根节点
        insert(root[i], 1, bl, find(a[i])); //向版本i中增加find(a[i])的值
    }

    int k = 0;
    for (int i = 1; i <= m; i++) {
        int x = read();
        printf("%d\n", b[query(root[0], root[x], 1, bl, ++k)]);
    }
    return 0;
}