cf1354 D. Multiset(二分)

题意:

给定长为 \(n\) 的数组 \(a[]\)\(1\le a_i\le n\)。接下来 \(m\) 次操作,每次操作输入 \(b_i\),有两种类型:

\(1\le b_i\le n\),表示向数组中插入 \(b_i\)

\(b_i<0\) ,表示移除数组中的一个第 \(|b_i|\) 小的数(保证该操作有效)

只需输出最终数组中的任意一个数

\(1\le n, m\le 1e6\)

思路:

可以在 \([1,1e6]\) 值域上开树状数组,然后二分找第 \(-b_i\) 小的数,十分简单。但有更快的做法:

二分找最大的位置x,满足 “最终数组中比x小的数为0个”。注意x不必是数组中实际存在的数,只是值域上的一个位置。遍历数组,记录比x小的数的数量lesscnt,遇到移除操作时仅移除排名<=lesscnt的

这样就能找到最终数组中的最小数

const int N = 1e6 + 5;
int n, m, a[N], b[N];

bool pd(int x)
{
    int lesscnt = 0;
    for(int i = 1; i <= n; i++)
        lesscnt += (a[i] < x);
    for(int i = 1; i <= m; i++)
        if(b[i] > 0) lesscnt += (b[i] < x);
        else lesscnt -= (-b[i] <= lesscnt);

    return !lesscnt;
}

signed main()
{
    iofast;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= m; i++) cin >> b[i];

    int l = 1, r = n + 1;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(pd(mid)) l = mid; else r = mid - 1;
    }

    if(l > n) l = 0;
    cout << l;
}

posted @ 2022-02-24 10:56  Bellala  阅读(23)  评论(0)    收藏  举报