【二分】CF1354D Multiset

传送门

题目描述

注意,本题的内存限制不同于常规题目。

给定一个包含 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);
}
相关推荐
哥不想学算法6 小时前
【C++】字符串字面量拼接
开发语言·c++
cookies_s_s11 小时前
C++ 字符串动态创建对象 -- 工厂模式、自动注册、模板递归动态调用
服务器·开发语言·c++
王老师青少年编程11 小时前
2026年6月GESP真题及题解(C++七级):消消乐
数据结构·c++·算法·真题·gesp·2026年6月
海清河晏11112 小时前
数据结构 | 二叉搜索树
数据结构·c++·visual studio
盐焗鹌鹑蛋12 小时前
【C++】继承
开发语言·c++
会周易的程序员13 小时前
使用 pybind11 封装 C++ 日志库 microLog 为 Python 模块
java·c++·python·物联网·架构·日志·aiot
WZF-Sang13 小时前
网络基础——2
服务器·网络·c++·学习·网络编程·php
-西门吹雪15 小时前
现代C++ 并发编程-学习指南
java·jvm·c++
没头脑的男大15 小时前
CMake学习教学
linux·c++·学习
aaPIXa62216 小时前
C++ TensorRT Edge-LLM 边缘推理框架:从原理到实战
开发语言·c++·edge