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;
}

浙公网安备 33010602011771号