IOI 2022 题解 & 锐评


IOI 2022 D1T1 Fish

题目大意:

有一个 /(N/times N/) 的网格,其中的 /(M/) 个位置有垒球,第 /(i/) 个垒球的位置为 /((x_i,y_i)/),重量为 /(w_i/)。

你可以为每一列 /(c/) 选择一个前缀的行 /(1,2,/ldots,/ldots,r_c/) 修建长堤,这样 /((1,c),(2,c),/ldots,(r_c,c)/) 这些位置就会被长堤覆盖。

如果一个垒球的位置没有被长堤覆盖,且其左右相邻的位置至少有一个被长堤覆盖,那么就会有一个 ix35 走到长堤上捡起这个垒球,即如果垒球的位置为 /((x,y)/),它能被捡起当且仅当 /((x,y)/) 未被长堤覆盖且 /((x,y-1),(x,y+1)/) 至少有一个被长堤覆盖。

求最多能捡起总重多少的垒球。

/(N/leq 10^5,/ M/leq 3/times 10^5/)


题解:

假设 /(r_{c-1}<r_c>r_{c+1}/),那么第 /(c/) 列的垒球都不能被捡起,所以不如直接把第 /(c/) 列修到顶,即 /(r_c/) 可以改为 /(N/)。

假设 /(r_{c-1}>r_c<r_{c+1}/),那么第 /(c/) 列不如不修,所以 /(r_c/) 可以改为 /(0/)。

于是我们观察长堤的形状,发现修建了长堤的列可以分成若干连续段,每一段内 /(r_c/) 先递增后递减。

据此容易写出 DP,时间复杂度为 /(O(M/log M+N)/)。

IOI 2022 D1T2 Prison

题目大意:

有 /(500/) 个囚犯,还有一个房间,房间中有两个袋子 /(A,B/),分别装有 /(n_A,n_B/) 个垒球,此外还有一个数字 /(x/),初始为 /(0/)。囚犯们已知 /(1/leq n_A,n_B/leq N/) 且 /(n_A/ne n_B/),他们的目标是指出 /(n_A/) 和 /(n_B/) 的大小关系。

现在会按照随机的顺序每次叫一个囚犯进房间,此时囚犯可以看到数字 /(x/),并做出两种选择之一:打开袋子 /(A/),或者打开袋子 /(B/),即获知 /(n_A/) 或 /(n_B/)。

此时囚犯知道了 /(x/) 以及一个袋子中的垒球数量,他可以做出三种选择之一:指出 /(n_A<n_B/),或者指出 /(n_B<n_A/),或者将 /(x/) 修改为 /(x’/)(也可以 /(x’=x/))。

你需要构造一种策略,使得任意时刻 /(0/leq x/leq X/),且在 /(500/) 个囚犯都进去过一次之前得到 /(n_A,n_B/) 的大小关系。

/(N/leq 5000/),当你构造的 /(X/) 满足 /(X/leq 20/) 时得满分。


题解:

这里写的就是我的思考过程。

首先考虑一个最最 naive 的方法:考虑 /(n_A,n_B/) 的二进制表示,前两个人比较 /(n_A,n_B/) 的最高位,第三个和第四个人比较 /(n_A,n_B/) 的次高位,以此类推,那么可以实现如下:

  • 第 /(2i/) 个人离开房间后确保 /(x/) 为 /(3i/);
  • 第 /(2i+1/) 个人来到房间,会发现 /(x/) 为 /(3i/),于是查看 /(n_A/),如果 /(n_A/) 第 /(i/) 高位为 /(0/) 则将 /(x/) 改为 /(3i+1/),否则改为 /(3i+2/);
  • 第 /(2i+2/) 个人来到房间,根据 /(x/) 是 /(3i+1/) 还是 /(3i+2/) 就可以知道 /(n_A/) 第 /(i/) 高位是 /(0/) 还是 /(1/),他再查看 /(n_B/),如果这一位不同,那么可以直接得到答案,否则就把 /(x/) 改成 /(3i+3/),再比较下一位。

这样需要 /(X=38/) 左右。

我们将这方法略作修改,如果第 /(2/) 个人进来看到 /(n_A,n_B/) 最高位相同,并不是只能把 /(x/) 改成 /(3/) 然后交给下一个人,实际上他还掌握更多信息,比如 /(n_B/) 的次高位,于是我们将策略改成如下:

  • 第 /(1/) 个人来到房间,会发现 /(x=0/),查看 /(n_A/),根据最高位跳到 /(x=1/) 或 /(x=2/);
  • 第 /(2i/) 个人来到房间,根据 /(x/) 的奇偶性知道 /(n_A/) 当前比较位是 /(0/) 还是 /(1/),然后查看 /(n_B/),如果不同则直接返回,否则看 /(n_B/) 的下一位,根据 /(0/) 还是 /(1/) 跳到两个不同的 /(x/);
  • 第 /(2i+1/) 个人同理,不过查看的是 /(n_A/)。

这样只需要 /(2/log N/) 次,即 /(X=26/)。

再优化一下,发现这里采用二进制不如采用三进制,/(3^8>N/),所以只需要 /(X=3/times 8=24/) 即可。

再想一想,其实对于不同的数据范围可以采用不同的进制,于是我们考虑 DP,令 /(f(i)/) 表示 /(N=i/) 需要的最小的 /(X/),枚举将 /(i/) 分成 /(j/) 段,那么 /(f(i)/leftarrow f(/lceil/dfrac{i}{j}/rceil)+j/),这样应该可以 /(X=22/) 或者 /(X=21/)。

现在只需要卡一下常数就可以了,我们注意到如果第一个人进去发现 /(n_A=1/) 或者 /(n_A=N/) 都可以直接返回结果(因为保证 /(n_A/ne n_B/)),所以我们每次可以把边上两个数砍掉,于是转移变成 /(f(i)/leftarrow f(/lceil/dfrac{i-2}{j}/rceil)+j/),这样就能顺利得到 /(X=20/) 的解。

IOI 2022 D1T3 Towers

题目大意:

有 /(N/) 座塔,第 /(i/) 座塔高度为 /(h_i/),保证高度两两不同。

现在有 /(Q/) 个询问,每个询问给定 /(l,r,d/),询问从 /([l,r]/) 中至多可以选择多少个塔,使得他们两两可以 /(d/)-通信。

两座塔 /(i<j/) 可以 /(d/)-通信,当且仅当存在一座塔 /(k/) 使得 /(i<k<j/) 且 /(h_k-d/ge/max(h_i,h_j)/)。

垒球。

/(N,Q/leq 10^5/)。


题解:

对于一个确定的 /(d/),令 /(l_i/) 表示 /(i/) 左边第一个比 /(i/) 高了至少 /(d/) 的位置,/(r_i/) 表示 /(i/) 右边第一个比 /(i/) 高了至少 /(d/) 的位置。

那么两座塔 /(i<j/) 能 /(d/)-通信,当且仅当 /(r_i/leq l_j/),即它们对应的区间 /([l_i,r_i)/) 无交。

同时易证以下两个结论:

  • 如果 /(i<j/) 满足 /(l_j<r_i/),那么一定有 /(l_j/leq l_i/),即不可能两个区间既不为包含关系,又相交。
  • 如果对于某个 /(d/) 有 /([l_i,r_i)/subseteq [l_j,r_j)/),那么对于更大的 /(d/),这一关系也成立。

于是我们可以从小到大扫描 /(d/),维护当前没有包含任何其他 /([l_j,r_j)/) 的区间 /([l_i,r_i)/),根据第一个结论,它们是两两无交的。我们称这些区间为 /(d/) 对应的有效区间。

扫描过程中维护的方法是:用一个链表记录当前的有效区间,并且用优先队列记录每个区间在什么时间(即 /(d/) 增大到多少时)会和左边或右边合并,然后在合并时删去一个元素即可。由于询问在线,所以需要用主席树记录每个时刻的有效区间。

询问 /((L,R,d)/) 时,我们首先找到这个 /(d/) 对应的情况下,/([L,R]/) 中有多少个有效区间(一个有效区间 /([l_i,r_i)/) 在 /([L,R]/) 中定义为其中心点 /(i/) 在 /([L,R]/) 中)。

那么这些有效区间的中心一定是要选择的了,但是可能还不全面,因为左右两边可能还需要各补选 /(0/) 个或 /(1/) 个塔(这是因为可能某个有效区间与 /([L,R]/) 有交,但是其中心不在 /([L,R]/) 中,就没统计到)。

这个稍微讨论一下即可:

  • 如果 /([L,R]/) 中一个有效区间都没有,那么我们找到 /([L,R]/) 中高度的最大值 /(h_m/),显然最多只能在 /(m/) 左右各选一个,判断一下行不行即可;
  • 如果 /([L,R]/) 中有至少一个有效区间,那么我们找到第一个有效区间左侧的最大值 /(h_m/),考虑能不能在 /(h_m/) 左边多选一个即可(即 /([L,m-1]/) 的最小值是否不超过 /(h_m-d/)),右边同理。

用 ST 表预处理区间最值。

时间复杂度为 /(O((N+Q)/log N)/)。

IOI 2022 D2T1 Circuit

题目大意:

给定一个 /(N/) 个非叶结点,/(M/) 个叶结点的值,叶结点有初值 /(0/) 或 /(1/),某个非叶结点如果有 /(x/) 个儿子,则需要设置一个参数 /(y/in [1,x]/),表示如果其儿子有至少 /(y/) 个的值为 /(1/),那么它的值也为 /(1/)。

现在进行 /(Q/) 次操作,每次翻转编号在区间 /([l,r]/) 内的叶结点的初值(/(0/) 变成 /(1/),/(1/) 变成 /(0/)),然后询问有多少种为非叶结点设置参数的方案,使得根结点值为 /(1/),对 /(10^9+2022/) 取模。

/(N,M,Q/leq 10^5/)


题解:

如果你是赛后做题,那么解题的关键或许是看到排行榜——有一车人过了这题,说明这题是水题。

或者是觉得这题看上去非常不可做,因为编号区间和树的形态并无关联,我们不可能用任何树上的数据解构解决此题。

无论如何,我们可以猜想:每个非叶结点对答案的贡献独立,幸运的是这是对的,证明并不困难,略。

于是首先树形 DP 算出每个非叶结点单独为黑色时的答案,然后用线段树维护总的答案即可,时间复杂度为 /(O(Q/log M+N+M)/)。

IOI 2022 D2T2 Insects

题目大意:

现在有 /(N/) 个未知数 /(a_{1/ldots n}/),还有一台神秘机器,机器初始是空的,你可以进行下列三种操作:

  • 将一个数 /(a_i/) 加入机器;
  • 将一个数 /(a_i/) 取出机器;
  • 询问机器里出现次数最多的数的出现次数。

你需要求出 /(a_{1/ldots n}/) 中出现次数最少的数的出现次数,设每种操作进行次数的最大值为 /(Q/)。

/(N/leq 2000/),/(Q/leq 3N/) 可得满分。


题解:

首先可以想到一个 /(O(N^2)/) 次操作的算法:每次放一对数进去,就能知道它们是否相等。

还有一种 /(O(N^2)/) 次操作的算法(记为青蛙算法):每次选择一个数,然后得到与它相等的所有数(加入一个别的数如果众数出现次数没有增加则说明不相等)。

还有一种 /(O(N^2)/) 次操作的算法(记为垒球算法):每次选择当前剩下的所有不同的数各一个(时刻保持众数出现次数为 /(1/) 即可),这样当某一次得到的不同的数的个数和上一次不同时,就知道出现次数最少的已经取完了。

综合青蛙算法和垒球算法,可以得到 /(O(N/sqrt N)/) 的算法。

考虑垒球算法的扩展:如果保持众数出现次数始终小于等于 /(C/),就可以得到每个数的前 /(C/) 次出现,设不同的数有 /(x/) 种,如果选出了恰好 /(Cx/) 个数则说明出现次数最少的数也出现了 /(/ge C/) 次,否则说明是 /(<C/) 次。

所以可以二分,得到了 /(O(N/log N)/) 的垒球算法。

然后发现这个二分的操作次数实际上可以变成 /(O(N)/),因为如果二分之后发现答案 /(/ge C/),那么此时已经放进机器里的就不用动了,可以看成总数大致减半;如果二分之后答案 /(<C/),那么此刻不在机器里的数就可以全部扔掉了,总数也大致减半,根据等比数列求和这就是 /(3N/) 次左右的。

然后一交获得了 /(99/) 分的好成绩,下面使用你的独门技巧乱搞卡常即可。这里我的方法是在判断时只要某个时刻机器里已经有 /(Cx/) 个数就直接退出循环,没必要加剩下的数,同时为了防卡,初始序列要随机重排。

IOI 2022 D2T3 Islands

题目大意:

有一张 /(N/) 个点,/(M/) 条边的有向图,你需要从点 /(1/) 出发走一些边回到点 /(1/),要求:

  • 一条边经过后就会反向,也就是下次经过必须是从反方向经过;
  • 不能连续经过同一条边。

构造一组方案,或说明无解。

/(N/leq 10^5,/ M/leq 2/times 10^5/)


题解:

如果一个结点出度为 /(0/),那么走到这里就寄了,所以可以删掉。

删掉之后可能会导致另一些点出度也变为 /(0/),也要删掉,这个可以做一次拓扑排序,就相当于我们删掉了所有不能走到环的点。

现在我们断言,如果点 /(1/) 的出度 /(/ge 2/),那么一定有解,下面给出构造。

事实上考虑下列结构:由于不存在零出度点,所以我们可以为每个点(除了 /(1/))指定一条出边,这样整张图就形成了若干个基环树,以及一棵以 /(1/) 为根的树,如下图所示:

IOI 2022 题解 & 锐评

现在我们加上 /(1/) 的两条出边,那么有以下两种情况:

  • Case 1:至少一条出边指向以 /(1/) 为根的树外。

    如下图所示,/(y,z/) 是出边到达的点,至少有一个在某个基环树上。

    IOI 2022 题解 & 锐评

    对于这种情况,我们先走到 /(y/),然后沿着继续走,在 /(y/) 所属的基环树上绕一圈,再回到 /(1/),现在整张图和初始情况相比就是 /(y/) 所在的基环树的环(红色环)被反向了。

    然后再走到 /(z/),同理绕一圈(蓝色环)再回 /(1/)。

    然后再走一次 /(y/),再绕一次红色环,这样红色环被绕了两次,就变回最初的方向了,同理再走一次 /(z/),使得蓝色环也回到最初方向。

  • Case 2:两条出边都指向 /(1/) 为根的树内。

    如下图左上部分所示:

    IOI 2022 题解 & 锐评

    这种情况我们考虑 /(y,z/) 的 LCA,如果 LCA 恰好是 /(1/),那么按照和 Case 1 相同的方法做就是没有问题的。

    否则我们换一种构造:首先走到 /(y/),然后沿着树上路径走到 /(x/),也就是绕了右上图的红色环。

    然后走到 /(z/),沿着树上路径走到 LCA,再从 LCA 走回 /(y/),再走 /(y/to x/) 这条边,如左下图的蓝色环。

    最后从 /(x/) 沿树上路径走到 LCA,再从 LCA 走到 /(z/),再走 /(z/to x/) 这条边,如右下图的红色环,构造完成。

    证明比较简单,注意图中 /(y,z/) 是动态变化的,在实现的时候,构造过程中的 /(y,z/) 始终是 /(x/) 的出边指向的点。

那么 /(1/) 的出度 /(/ge 2/) 的情况已经证完了,下面考虑 /(1/) 的出度 /(=1/) 的情况。

这时我们只能走这条出边,设为 /(1/to x/),之后 /(1/) 就可以看出零出度点(只要在不走 /(x/to 1/) 这条反向边的情况下回到 /(1/) 就寄了),所以我们删去点 /(1/),同样这也会导致连锁反应,还是用一个拓扑排序就可以把所有该删的点都删了。

在剩下的新图上,把 /(x/) 看成起点继续做即可,即如果 /(x/) 的出度 /(/ge 2/) 那么按照上面方法得到构造;如果 /(x/) 的出度 /(=1/) 那么再走它的出边,然后把 /(x/) 删掉,以此类推。

时间复杂度应该可以做到 /(O(N+M)/),不过我在实现时为了图省事,在记录边的序号以及删点的时候用到了 multisetmap,复杂度为 /(O(N+M/log M)/)。


锐评环节

D1T1:比较简单的题,大概是想到单调性之后随便 DP 一下就行了,不过如果没想明白的话可能会用线段树去维护,其实是不用的。

D1T2:人类智慧题,有点看运气,运气好就想出来罢了,主要是到 /(X=26/) 那一步比较有难度,后面的使用其他进制并 DP 以及掐头去尾其实都不是很难。

D1T3:偏简单题,主要是想到用区间表示每个点的状态,后面的数据结构部分非常简单。

D2T1:诈骗题,猜出结论就没有难度了,而想到去猜这个结论其实并不很难(正如我前面所说,如果没有这种结论,这题是不可做的)。

D2T2:中档题,每一步优化都不是很难,但是只要少一步就做不出来,然后最后还要为了零点零几分在那卡常。

D2T3:思维简单,代码不太好写题,感觉这题口胡起来非常舒服。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/280271.html

(0)
上一篇 2022年8月13日
下一篇 2022年8月13日

相关推荐

发表回复

登录后才能评论