CEOI2024
Day1 T1 海战
不难发现,如果两艘船会相遇,那么他们之间必然要满足某些要求。首先同向的之间必然不会相遇,然后就可以分 \(6\) 种情况讨论。(下文中认为 \((x_1,y_1)\) 为与前的一项,\((x_2,y_2)\) 为与后的一项)。
N
与S
:要求 \(x_1=x_2\) 且 \(y_2<y_1\),相遇时间为 \(\dfrac{1}{2}(y_1-y_2)\)。N
与E
:要求 \(x_1-y_1=x_2-y_2\) 且 \(x_2<x_1\),相遇时间为 \(x_1-x_2\)。N
与W
:要求 \(x_1+y_1=x_2+y_2\) 且 \(x_1<x_2\),相遇时间为 \(x_2-x_1\)。S
与E
:要求 \(x_1+y_1=x_2+y_2\) 且 \(x_2<x_1\),相遇时间为 \(x_1-x_2\)。S
与W
:要求 \(x_1-y_1=x_2-y_2\) 且 \(x_1<x_2\),相遇时间为 \(x_2-x_1\)。E
与W
:要求 \(y_1=y_2\) 且 \(x_1<x_2\),相遇时间为 \(\dfrac{1}{2}(x_2-x_1)\)。
我们要处理所有相撞的过程,可以考虑如下维护:找到当前最早的相撞时间 \(t\),找到所有在 \(t\) 时刻会相撞的船,将所有这些船,
发现这六种情况都是根据某一个关键值划分,然后将所有的元素按照权值 \(val\) 从小到大排序,元素分为两种状态 \(0\) 和 \(1\),要找到一个 \(0\) 的 \(x_1\) 和一个 \(1\) 的 \(x_2\),有 \(x_1<x_2\),那么他们相遇的时间为 \(x_2-x_1\)。
这个问题可以使用线段树维护,而找到所有相撞时间为 \(t\) 的可以通过在线段树上搜索做到 \(O(cnt\log n)\) 的复杂度,其中 \(cnt\) 为找到的满足条件的 \(x_1,x_2\) 数对数。而 \(O(\sum cnt)=O(n)\),所以最终复杂度为 \(O(n\log n)\)。
Day1 T2 核酸检测
考虑我们做的整个过程必然是逐个找到阳性的人。发现我们的过程都可以被描述成如下的状态:有 \(i\) 个人,其中前 \(j\) 个人中有阳性的人。如果 \(j=0\) 表示不确定任何这 \(i\) 个人的子集的状态。
不妨设 \(f_{i,j}\) 为上面对应状态的 \(i,j\) 期望需要多少次询问。
如果 \(j=0\),则我们枚举询问的人数 \(k\),如果这 \(k\) 个人中有阳性,则转移到 \(f_{i,k}\),否则转移到 \(f_{i-k,0}\)。所以有 \(f_{i,0}=1+\min\limits_{1\le k\le i}\left([1-(1-P)^k]f_{i,k}+(1-P)^kf_{i-k,0}\right)\)。
如果 \(j=1\),这个人必然是阳性,则有 \(f_{i,1}=f_{i-1,0}\)。
如果 \(j>1\),我们假设询问这 \(j\) 个人中的 \(k\) 个人,如果这 \(k\) 个人中有阳性,则转移到 \(f_{i,k}\),否则转移到 \(f_{i-k,j-k}\)。所以有 \(f_{i,j}=1+\min\limits_{1\le k\le j}\left(\dfrac{1-(1-P)^k}{1-(1-P)^j}f_{i,k}+\left[1-\dfrac{1-(1-P)^k}{1-(1-P)^j}\right]f_{i-k,j-k}\right)\)。
这个 DP 的复杂度为 \(O(n^3)\) 的,求出之后记录转移来的 \(k\),那么最优的期望操作次数就是 \(f_{n,0}\) 的值。由于数据完全随机,所以可以根据这个决策进行询问。
将题目所有的 \(P\) 带入计算,发现都刚好比 \(F\) 小 \(O(1)\)。
Day1 T3 文本编辑器
称每一行的第一个和最后一个位置为特殊位置。
考虑将过程分成两种情况:不经过特殊位置;经过特殊位置。
对于第一种情况,可以判断这种情况是否存在,如果存在,那么移动的步数就是两点间的哈密顿距离。
对于第二种情况,可以拆分成从起点走到一个特殊位置;在特殊位置之间移动;从一个特殊位置走到终点。
对于第一步和第三步可以 \(O(n)\) 处理,考虑对于第二步建立最短路模型,考虑有哪些有效的移动:
- 从第一个位置走到第一个位置,只需要考虑相邻的两行,距离为 \(1\)。
- 从第一个位置走到最后一个位置,考虑从上一行的最后一个换到下一行的第一个,距离为 \(1\)。
- 从最后一个位置走到最后一个位置,对于每一个位置,发现只需要考虑它和他左边第一个比他短的和右边第一个比他短的,他们之间的移动距离可以 \(O(1)\) 计算。找的过程可以单调栈扫两边就可以了。
最短路直接跑 Dijkstra 即可,时间复杂度 \(O(n\log n)\)。
Day2 T1 玩具谜题
考虑维护中线点移动过程。考虑两个相邻的位置都作为中心点是否可以移动,发现就是首先要求这个位置要能够容纳这个玩具。同时这两个各自之间的位置要求能够将对应的块划过去,也就是两列都空出来的位置要能够让竖着放的滑块横着滑过去。
这个东西可以正着扫一边然后倒着扫一遍维护出来,然后就是检验起点和终点是否联通了,时间复杂度 \(O(WH)\)。
Day2 T2 加油站
要处理树上所有路径的问题,考虑点分治。那么当前只考虑跨过分治中心的路径。
对于某一个点 \(u\),可以将路径分成两种情况:从 \(u\) 的子树内经过分治中心到其他子树,从其他子树经过分治中心到达 \(u\) 子树内。
对于第一种情况,发现经过分治中心之后有多少个点是无关紧要的,所以只需要考虑子树内有多少个点往上会在 \(u\) 停留。考虑维护数组 \(f_u\) 表示 \(u\) 的子树内有多少个点出发的车会在 \(u\) 停留,发现 \(f_u\) 会转移到距离他 \(\le k\) 的最浅的祖先上,这个过程可以使用倍增维护。
对于第二种情况,考虑对于一个点,我们只有确定了路径上的下一个点的距离,才能够确定有哪些点会在这个位置加油。考虑 dfs 整棵树的过程,可以实时维护出这样的路径。也就意味着从 \(u\) 走到它的一个孩子 \(v\) 的时候,我们可以确定有哪些点在 \(u\) 处会加油,而这些点的终点有 \(siz_v\) 中情况。而求出有多少个点会在某个地方加油,可以先预处理出来从每一个位置出发的车到了分治中心还剩下都油,就可以根据这个在分治中心前设立一些虚拟的起点,让这些车从对应起点出发,在不加油的情况下刚好在分治中心处剩下对应数量的油。然后在枚举 \(u\) 的儿子 \(v\) 的时候就可以通过在序列上二分来找到前面哪一段区间的节点会在 \(u\) 节点停下来加油。
单层的时间复杂度为 \(O(n\log n)\),套上点分治之后时间复杂度为 \(O(n\log^2n)\)。
Day2 T3 洒水器
考虑二分答案,问题变成如何 chk 某一个 \(K\) 是否合法。
考虑从左往右确定每一个洒水器的方向,最大化已经覆盖了的花的数量。那么发现,如果当前洒水器没有必要向左,那么向右一定是不劣的。
如果这个向左,可能会出现如下的情况:调整成第 \(i\) 个向右,但是第 \(i+1\) 个向左更优。我们还是当作两个都向左处理,但是维护花的指针记录到 \(i\) 向右的位置,同时在 \(i+1\) 的位置打上标记。在扫描完之后对于一部分的花进行翻转即可。
时间复杂度 \(O((n+m)\log V)\)。