1 条题解
-
0
自动搬运
来自洛谷,原作者为

Maxmilite
**搬运于
2025-08-24 21:14:25,当前版本为作者最后更新于2022-12-11 22:49:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
B3691 & P8889 [语言月赛202212] 狠狠地切割 题解
Source & Knowledge
2022 年 12 月语言月赛,由洛谷网校入门计划/基础计划提供。
文字题解
题目大意
给定一个长度为 的序列 和 个互不相同的整数 。对序列 中的每个位置,如果这个位置的数在 中出现过,则在此进行一次切割。求最后序列被分割为了多少片段。
解析
本题的任务我们可以划分为两部分,第一部分为找到应该切割的位置,第二部分为计算片段数量。
第二部分较为简单,且实现方法多样,故这里不做过多的介绍,核心代码如下:
int cnt = 0; // 标记当前一段片段的长度 for (int i = 1; i <= n; ++i) { if (a[i].isMarked) { // 使用 isMarked 记录当前点是否是切割点 if (cnt) // 判断该切割点前是否存在片段 ++ans; cnt = 0; } else { ++cnt; } } if (cnt) // 需要注意的是,序列的结尾不一定为切割点,所以这里要特殊处理一下 ++ans;重点放在第一部分「找到应该切割的位置」,这里介绍几种方法。
-
Easy Version 方法:使用数组记录
我们注意到,在 Easy Version 中,保证给定的 值域为 ,且题目内存限制为 512MB,因此我们可以直接开一个大小为 的数组 ,如果数字 在 中出现过,则 标记为 ,反之标记为 。
在第二部分,我们只需要查找 是否为 ,即可知道 是否在 中出现过。
-
Hard Version 方法:
unordered_map等在 Hard Version 中, 值域变为了 ,因此我们不能直接使用数组记录了。
我们可以采用一些 STL 容器来进行这些操作,这里举例介绍一下使用
map / unordered_map的具体流程。在本题中对 中的元素,你可能需要存储一些形如
[998244353..., true]的键值对,但是由于数组下标大小的限制,所以无法直接用元素作为下标来存储。这个时候,我们可以使用
map / unordered_map将这些键值对进行存储。map重载了operator[],可以使用任意定义了operator<的类型作为下标,诸如这里的 。具体的,对于上面诸如
[998244353..., true]的键值对,我们可以按照如下方式进行存储和查询:unordered_map<long long, bool> v; v[998244353] = true; if (v[998244353]) { /* ... */ }如果想对
map进行进一步的学习,可以参照 https://oi.wiki/lang/csl/associative-container/ 及 https://oi.wiki/lang/csl/unordered-container/,这里不再深究。因此本题对于 中数据的记录,我们可以使用
unordered_map完成。使用map在实现优秀的条件下可能也可以通过本题。 -
Hard Version 方法:二分查找
我们可以将 排序,对于每个 中的元素,在 中进行二分查找是否存在即可。
这里不再对二分查找做过多的介绍。
-
Hard Version 方法:双指针
我们可以将 由小到大分别排序,建立指针
cur。对 中每个元素 ,在 中找到第一个 的元素的下标。并用cur对其记录。在查找下一个元素 时,由于上一次的指针位置前的元素一定 ,因此只要从上一次的指针位置开始向后查找即可。不难发现,最劣情况下,数组 只会被遍历一次。
出题人使用的是这种办法,核心代码如下:
struct Node { int num; lint val; int isMarked; } a[1000005]; scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { lint var; scanf("%lld", &var); a[i] = Node(i, var); } for (int i = 1; i <= m; ++i) { scanf("%lld", b + i); } b[m + 1] = (1ll << 60); b[0] = -(1ll << 60); sort(a + 1, a + n + 1, cmp); sort(b + 1, b + m + 1); for (int i = 1; i <= n; ++i) { while (b[cur] < a[i].val) ++cur; if (b[cur] == a[i].val) a[i].isMarked = 1; }
视频题解
完整代码请在视频中查看。
-
- 1
信息
- ID
- 8216
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 2
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者