题目描述
注意,本题的内存限制不同于常规题目。
给定一个包含 n n n 个整数的多重集。你需要处理两种类型的操作:
- 向多重集中添加整数 k k k;
- 查询并删除多重集中第 k k k 小的数。
多重集中的第 k k k 小数,指的是将多重集中的所有元素从小到大排序后,第 k k k 个元素。例如,如果多重集包含元素 1 1 1、 4 4 4、 2 2 2、 1 1 1、 4 4 4、 5 5 5、 7 7 7,且 k = 3 k=3 k=3,那么排序后为 [ 1 , 1 , 2 , 4 , 4 , 5 , 7 ] [1, 1, 2, 4, 4, 5, 7] [1,1,2,4,4,5,7],第 3 3 3 小的数是 2 2 2。如果要删除的元素在多重集中出现多次,仅删除其中一个。
在所有操作完成后,输出多重集中的任意一个数。如果多重集为空,输出 0 0 0。
输入格式
第一行包含两个整数 n n n 和 q q q( 1 ≤ n , q ≤ 10 6 1 \le n, q \le 10^6 1≤n,q≤106),分别表示初始多重集中的元素个数和操作次数。
第二行包含 n n n 个整数 a 1 , a 2 , ... , a n a_1, a_2, \ldots, a_n a1,a2,...,an( 1 ≤ a 1 ≤ a 2 ≤ ⋯ ≤ a n ≤ n 1 \le a_1 \le a_2 \le \dots \le a_n \le n 1≤a1≤a2≤⋯≤an≤n),表示多重集中的元素。
第三行包含 q q q 个整数 k 1 , k 2 , ... , k q k_1, k_2, \ldots, k_q k1,k2,...,kq,每个整数代表一个操作:
- 如果 1 ≤ k i ≤ n 1 \le k_i \le n 1≤ki≤n,则第 i i i 个操作为"将 k i k_i ki 插入多重集";
- 如果 k i < 0 k_i < 0 ki<0,则第 i i i 个操作为"删除多重集中第 ∣ k i ∣ |k_i| ∣ki∣ 小的数"。对于此操作,保证 ∣ k i ∣ |k_i| ∣ki∣ 不大于当前多重集的大小。
输出格式
如果所有操作后多重集为空,输出 0 0 0。
否则,输出多重集中任意一个元素。
输入输出样例 #1
输入 #1
5 5
1 2 3 4 5
-1 -1 -1 -1 -1
输出 #1
0
输入输出样例 #2
输入 #2
5 4
1 2 3 4 5
-5 -1 -3 -1
输出 #2
3
输入输出样例 #3
输入 #3
6 2
1 1 1 2 3 4
5 6
输出 #3
6
说明/提示
在第一个样例中,多重集中的所有元素都被删除了。
在第二个样例中,元素 5 5 5、 1 1 1、 4 4 4、 2 2 2 被依次删除。
在第三个样例中, 6 6 6 不是唯一的答案。
由 ChatGPT 4.1 翻译
题解
wakakaka,没想到时隔n年上大学又来写题解了qwq
一开始以为是线段树,一看内存估计是超了
暴力按照题目要求模拟,时间应该是 O ( n 3 ) O(n^3) O(n3)
后面想到既然只是看有没有数最后还在,那不是只要遍历一遍数集,判断每个数有没有被前后删掉不就好了,可是 O ( n 2 ) O(n^2) O(n2)肯定还是超时的
这时仔细阅读题目发现,这个数集里的数都是1到n的,所以不管什么大小排序或者何时加入进来的,最后剩下来的数本质就是一个在1到n的数
其实根本不用管数集里面那些的"数字个体",而是判断一个在1到n的数最后有没有留下来就可以了
二分答案
check(mid)判断mid有没有被前删
记录≤mid的数的数量,如果被从前面删掉了,那么比mid小的数肯定都会被删掉,所以答案肯定就在mid后了
以此为思路找到最小的那个没被删掉的数
Code
cpp
#include <bits/stdc++.h>
#define N 1000010
using namespace std;
int n, m, l, r, mid, ans;
int a[N], q[N];
bool check(int x) {
int qian = 0;
for(int i = 1; i <= n; i ++)
if(a[i] <= x) qian ++;
for(int i = 1; i <= m; i ++)
if(q[i] < 0) {
if(-q[i] <= qian) qian --;
} else
if(q[i] <= x) qian ++;
return (qian);
}
int main() {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
int ls = n;
for(int i = 1; i <= m; i ++) {
scanf("%d", &q[i]);
if(q[i] < 0) ls --; else ls ++;
}
if(!ls) {
printf("0");
return 0;
}
l = 1, r = n;
while(l <= r) {
mid = (l + r) >> 1;
if(check(mid)) {
ans = mid;
r = mid - 1;
} else l = mid + 1;
}
printf("%d", ans);
}