Java教程

IOI 2022 题解 & 锐评

本文主要是介绍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\) 为根的树,如下图所示:

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

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

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

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

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

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

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

    如下图左上部分所示:

    这种情况我们考虑 \(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:思维简单,代码不太好写题,感觉这题口胡起来非常舒服。

这篇关于IOI 2022 题解 & 锐评的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!