UOJ NOI Round6 题解
为什么打得不好?
因为两天 T1 一共得了 40 分。
Day1T1 面基之路
对于一个特定的时间 \(t\),如果存在一个点使得它到每个关键点的距离都不超过 \(t\) ,那么就一定合法。同时最小满足条件的 \(t\) 就是答案。否则存在一个点找不到大部队
求出来最多 \(K+1\) 个有人的点到 \(1\sim n\) 的最短路。对于图上每个点求出来最小的 \(t\) 再取 \(\min\) 即可。如果这个点是给定的 \(n\) 个点之一容易处理
对于边 \((u,v)\) 求答案时,将所有 \(K+1\) 个点到 \(u\) 的距离从小到大排序。枚举一个后缀从 \(v\) 处进入边 \((u,v)\) 。那么求出来从 \(u\) 进的点中最晚的一个和从 \(v\) 进中最晚的点的相遇时间即可。注意相遇点可能是 \(u/v\)
对于答案乘 \(2\) 可以将输入的边权乘二
Day1T2 机器人表演
最终生成的某个字符串 \(T\) 合法性判定本质上是以下过程:
设已经处理的 \(T_{1\dots i}\) 匹配了 \(S_{1\dots j}\) ,同时剩下了 \(k(k\ge 0)\) 个 \(0\) 没有配对。现在加入 \(T_{i+1}\):
如果 \(T_{i+1}=S_{j+1}\) 则 \(j'\leftarrow j+1,k'\leftarrow k\) 。
否则如果 \(k>0\) 则 \(j\leftarrow j,k\leftarrow k-1\) 。如果原先的匹配方案下也不剩下什么 \(0\) 了,就把前缀 \(S_{1\dots i}\) 的第一个满足 \(0\) 的数量 \(>1\) 的数量的后缀撤销匹配。让这个 \(0\) 撤出来和 \(T_{i+1}\) 匹配。
让 \(0\) 的数量 \(>1\) 的数量的原因是 \(S\) 中的 \(1\) 已经在 \(T\) 中匹配了,否则前面的 \(1\) 还得重新找 \(0\) ,自然在 \(<i\) 的某个位置找不到匹配了。这样的匹配长度一定最长。
于是设 \(f_{i,j,k}\) 模拟上面的过程。预处理每个前缀最短后缀满足让 \(0\) 的数量 \(>1\) 的数量。时间复杂度 \(\Theta(n^3)\)
Day1T3 稳健型选手
原题题意本质上是每个前缀 QAQ 蚤不能取超过一半。询问区间长度为奇数时让先手最后一次操作取末尾元素,也就变成了区间长度偶数的情况
第一种贪心:从后往前两个一组遍历元素,用大根堆维护所有元素,每加入两个弹出一个。
第二种贪心:前缀选不超过一半等价于后缀不选的数量不超过一半。正序遍历,两两一组。用小根堆维护不选的元素。
分治。
模拟第一种贪心倒序扫 \([L,mid]\) 并使用可持久化线段树维护每个 \([k,mid]\) 选择了哪些数字。
模拟第二种贪心正序扫 \([mid+1,R]\) 并另维护每个前缀没有选择哪些数字。
合并就是将 \([L,mid]\) 被选中元素 的较小的一部分换成 \([mid+1,R]\) 没被选元素中较大的一些。此时权值线段树上存的都是能选的,要把前缀选哪些求出来。线段树上二分。
Day2T1 小火车
如果可以找到两个 \(\sum a\) 相同的无交子集 \(S,T\) 可以让 \(S\) 中元素 \(b_i\) 为 \(1\) ,\(T\) 中为 \(-1\) 来走回原点。注意到每个子集的 \(\sum a\) 在模 \(p\) 意义下 \(\in[0,p-1]\) 。
只有 \(p\) 种可能!看看这个 \(2^n>p\) 那么 Impossible 就成了诈骗。因为鸽巢原理。那么尝试求解哪个 \(\sum a\) 出现了两次。其实只要出现就可以,子集 \(S,T\) 存在交集时将其变成对称差就行了。
做法是二分答案:求有几个子集 \(S\) 满足 \(\sum a\in[l,r]\) ,这个可以折半后双指针。
Day2T2 神隐
用 \(\binom{20}{10} \text{ or } \binom{14}7\) 的思想给每条边标号。进行 20 或 14 次询问,第 \(i\) 次将新标号第 \(i\) 为 \(1\) 的边加入询问。
一个平方暴力就是将每次询问的得到的所有联通块集合的元素包含的点对进行标记。如果某个点对被标记了 10 或 7 次那么他俩之间一定存在一条边。
优化的方向主要是快速查询两个点被多少联通块同时包含了。
尝试剥叶子。某个点是叶子的条件是它作为单点联通块出现了 10 或 7 次。可以拓扑排序得到删除叶子序。注意这个序列上最后两个点在删除时都是叶子,必然相连。
在某个叶子 \(x\) 从他所在的联通块中删除时该联通块只剩下一个点 \(y\),那么必然存在边 \((x,y)\),也就是说找到了 \(x\) 在树上唯一联通的点。那么与之俱来的问题就是找不到 \(y\)。这个问题可以对于每个联通块维护一个 “已经被剥去且没有连边的叶子”。当某个点找到 \(y\) 时,找到所有 \(y\) 作为最后一个被删去点的联通块的没连边叶子连边即可。
正确性在于每条叶父边必然存在一个二进制位满足父祖边是断开的。
Day2T3 Border 的第五种求法
Border 也可以被刻画到 SAM 上。
找到 \(S_{[l,l]},S_{[l,l+1]}\dots S_{[l,r]}\) 在 SAM 上的位置,如果它们出现在 \(S_{[1,R]}\) 在后缀树的根链上就说明这是 Border。而求答案就相当于是求出这些交点的 \(\rm endpos\) 大小和
直接的二位数点想法需要将两个链都剖成 \(\log\) 段,但是由于是根链,所以可以将所有求交需求离线放到 \(S_{[1,r]}\) 。\(\rm dfs\) parent 树并维护在树状数组中维护根链上的点在 DAG 上的 \(\rm dfn\) 序并完成求交即可
时间复杂度 \(\Theta(n\log n+q\log^2n)\)
找区间 \(S_{[l,l]}\dots S_{[l,r]}\) 在 DAG 上对应的链的工作对应到实现上不那么容易。想要知道重链失配的位置需要另写一个求 \(\rm LCP\) 的工具,反串 SAM 或者 SA 都行。如果是不增加复杂度的哈希二分就要注意:由于某个点可以作为多个前驱的重儿子,于是要选择一个源点到它串数最多的父亲来得到类 \(\rm dfn\) 序。