概况
今年的网络赛也算告一段落了,我们队拿到了上海站的区域赛名额,比赛在 12 月初,还有不少时间可以准备。
接下来的这段时间里,有空可能会看看上海站往年的题目,熟悉一下题目的风格,不过在此之前先复盘一下刚刚参加的两轮网络赛。
ICPC 第二场网络赛题目相当难,不过 CCPC 网络赛的题相对简单一些,就先从这次的 CCPC 网络赛开始复盘。
ICPC 第一次网络赛的复盘可见我的另一篇博客。
这次我们做了 8 道题出来,发挥还算不错,保持这个状态拿区域赛银牌应该问题不大。
根据外部榜单,题目的通过情况如下:
A:
B:
C:
D:
E:
F:
G:
H:
I:
J:
K:
L:
M:
N:
以上标绿色的题目是我们做出来的,标橙色的是相对可做但没做出来的,标红色的是相对不可做的,基本都是省选以上难度的题目,可见这次把可做的题目都做得差不多了。
详细题面见 QOJ,此处不再赘述,接下来记录一下可做的 9 道题目解法,根据通过人数,按简单到困难的顺序记录。
Problem N
签到题,按题意模拟。
Problem B
暴力搜索 个所有可能的组合,并检查每个组合的 种点数对,如果最小可能点数和比给定的要求要小,差值可任意分配。
Problem H
通信题,做法很多,可以第一轮输出原排列及每个 分别和 交换的排列。第二轮恢复的时候 只需要找对应列的众数, 是剩下没有被使用的数字。
Problem I
注意到满足条件的 一定是偶数,对于偶数可直接构造 。
Problem E
通过观察可以发现,位置 后有效边界存在的充要条件是,三个排序中有两个具有相同的前 个问题集合。
标答的做法是取一对排名 ,记录每个元素在 中的位置,然后从左往右扫描 ,维护它的前 个元素在 中的最大位置,当最大值等于当前扫描到的位置 时,说明 后面存在有效边界。
对三个排名两两之间做以上操作,最后统计所有满足条件的 即可,时间复杂度为 。
我们的做法是从左到右同时扫描三个排名,分别维护三个排名前缀集合两两之间的对称差大小,实现也很简单,时间复杂度为 。
Problem K
从出度为 的 点开始按照拓扑排序,依次维护每个点到 的最短距离和最短路径条数,再计算期望,根据节点是否有信号决定转移的系数,无信号时均分,有信号则从所有最短路中按照条数加权分配,时间复杂度为 。
虽然这道题的思路很简单,不过我不太熟练图论相关的程序实现,代码是交给队友写的。
Problem C
从这题开始就需要一些思考了,考场上我在做 题,这题是完全交给队友做的。
首先到达 后,一定沿着垂直方向抵达终点,而抵达 的路径也一定是沿着一条直线,如果是曲线变成直线肯定更优。
接下来需要思考穿过 前,是否需要等待障碍物离开,实际上等待不可能发生,如果发生等待,那一开始不应该往你等待的位置走,至少往这一时刻的端点位置走会是更优的选择。
因此最终跑法很简单,要么直接垂直跑向终点,要么先直线走到 ,且此时在障碍物的端点处,然后再垂直跑向终点,
对于第二种情况列出方程 和 ,取最小非负实根即可,时间复杂度 ,代码实现不算难。
Problem M
组合计数题,首先对数组排序,然后进行枚举。
第一种情况,若子序列长度为奇数,且 作为唯一的中位数,则需要在左右两边各选 个元素,方案数如下:
对于组合数的计算,可以先预处理 到 的阶乘,然后每个组合数只需要 的时间复杂度。
但是直接算出这个求和式的每一项并相加,需要 的时间复杂度,一共有 个求和式,总复杂度为 ,需要进一步的优化,注意到:
因此每个求和式的计算被优化到了 ,总时间复杂度 。
第二种情况,若子序列长度为偶数,且 作为两个中间元素,同理方案数如下:
首先固定左边的 ,然后对于所有的 ,需要一次全部处理,假设这个值在排序后出现的位置区间为 ,注意到:
查询位置区间可用函数 lower_bound 和 upper_bound,二分查询的时间复杂度为 ,因此对于固定的 ,只需要 的时间就能完成所有方案的计算,总时间复杂度 。
而最开始对数组排序的时间复杂度为 ,综上,这道题的时间复杂度是 。
考场上这两个注意到比较难想,第一个是打印了很多求和式结果后发现的,第二个的灵感源自于高中时期做的数学题,代码实现不算难。
Problem J
这应该是考场上剩下的唯一一个有机会做出来的题目,可惜做到这题的时候时间已经所剩无几了。
首先可以发现流水线部分其实只和最大 batch 工作量有关,第一条流水线必须依次处理所有 batch,执行完毕的时刻为 。
现在考虑第一条流水线和第二条流水线之间的完成时间差 ,只有当 时,第二条流水线才不会被最大的 batch 卡住,以此类推,所有 条流水线执行完毕的时刻为 。
因此最终优化的目标只有 ,等价于把一个环分成不超过 段,最小化每段内数字之和的最大值,我们在考场上也只想到这里。
接下来考虑二分答案,答案的下界为 ,上界为 ,给定 ,先将环复制一遍变为 ,若起点已经确定从位置 开始,根据贪心应该尽可能顺时针扩展到最远位置,则找最大的 使得 。
记 ,如果到了最右边则记 ,这个 nxt 数组可以通过双指针一次 全部求出来,因为左指针和右指针都只会往一个方向移动,最多移动 次。
接下来只需要检查 的 级后继是否覆盖了一圈即可,即 ,但难点在于需要在线性时间内完成所有点的 级后继查询。
如果用倍增法,单次 check 需要 ,套上最外层二分后的总复杂度为 ,很可能超时。
标答用到了一些技巧,对于每一个 ,连一条边 ,一定有 ,而每个点最终一定会走到最右边 处,因此连成的边形成了一棵父亲编号始终更大的树,根节点为 。
接下来就只需要从根节点 DFS 遍历,同时维护当前 DFS 路径的数组,如果当前节点深度是 ,它的 级后继就是 ,因为每个节点只访问一次,一次检查只需要 ,总复杂度 。
总结
这次网络赛简单题目比较多,我们的发挥还算可以,不过还是在代码调试上花了过多时间,导致来不及做 J 题。
我个人认为代码的简洁性还是很重要的,简短的代码往往更易于调试,毕竟人的注意力窗口也有限,可以善用函数封装和 STL。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






