CTS2023
D1T1 琪露诺的符卡交换
考虑我们认为一个人的所有卡牌都是可以自由交换的,我们只需要在处理的时候交换下标即可,这是一个很松的限制。而每一个卡片只能交换一次,这个限制太紧了。
所以我们可以先交换一个人的所有卡牌的顺序,是的所有人的第 \(i\) 张牌这 \(n\) 张牌构成了 \(1\sim n\)。然后我们交换所有 \((i,j)\) 和 \((j,i)\) 即可。
现在的问题变成,每一个人剩余 \(k\) 张卡片,在所有人的手中总计有每种卡片 \(k\) 张时,如何从每一个人剩余的卡牌中选出 \(1\sim n\) 各一张。发现这是在二分正则图上的完美匹配问题,可以用 Hall 定理证明一定有解。
使用 Dinic 实现时间复杂度为 \(O(n^{3.5})\)。
D1T2 鸽子
D1T3 另一个欧拉数问题
上面两个题应该不会补
D2T1 楼梯
先解决如何求出答案:楼梯的边缘从左下到右上的边缘,向右走为 \(0\),向上走为 \(1\),那么我们就找到了一个长度为 \(p+1\) 的 \(01\) 序列,我们要找到其中的一个长度为 \(q+1\) 的 \(01\) 子序列,使得它以 \(0\) 开头,以 \(1\) 结尾。
取出原 \(01\) 序列的第 \(1,q+1,2q+1\dots p+1\) 位(保证了 \(q|p\)),由于第 \(1\) 位是 \(0\),最后一位是 \(1\),那么可以证明,必然存在某一个位置 \(i\),使得 \(i\) 位为 \(0\),\(i+1\) 位为 \(1\)。
具体的实现方式考虑二分。对于当前第 \(L\sim R\) 位,保证 \(L\) 位为 \(0\),\(R\) 位为 \(1\)。如果 \(R=L+1\) 就返回;否则找到 \(mid=\left\lfloor\dfrac{L+R}{2}\right\rfloor\) 查询它是 \(0\) 还是 \(1\),如果是 \(0\),就递归到 \([mid,R]\),否则递归到 \([L,mid]\)。
这也就意味着我们可以只查询 \(O(\log V)\) 个位置来得到答案。而查询某一个位置是 \(0\) 还是 \(1\) 可以使用线段树上二分来实现,单次复杂度为 \(O(\log V)\)。
发现维护这个楼梯的过程也可以用线段树维护,时间复杂度 \(O(m\log^2 V)\)。
D2T2 比赛
构造全错了,随机化全对了。
首先我们考虑什么情况下无解,因为相邻的三个人不能在同一个社团里面,所以在有一个社团的大小 \(> \dfrac{2}{3}n\) 的时候是无解的。
我们可以证明其他情况下一定有解:
如果所有社团的大小 \(\le 2\),可以直接构造。否则考虑在人数最多的社团 \(S_1\) 里面选出 \(2\) 个人,然后再从次大的社团 \(S_2\) 里面选出一个不在最大社团里面的人。我们递归构造其他 \(n-3\) 个人。
由于 \(|S_1|\le \dfrac{2}{3}n\),显然有 \(|S_1|-2\le \dfrac{2}{3}(n-3)\)。由于 \(S_2\) 是次大的,所以 \(|S_2|\le \dfrac{1}{2}n\),但我们同时要保证 \(|S_2|-1\le \dfrac{2}{3}(n-3)\)。计算得到 \(n\ge 6\)。
通过搜索证明 \(n<6\) 的时候有解,分类讨论证明一定存在将单独拎出来的三个位置连续插进序列的方式,就可以证明一定有解了。
但是发现实现找最大社团,实时删除人的这些操作比较不好实现,细节较多,所以考虑随机化。这里考虑使用爬山。
我们初始构造一个将人数最大的社团的人都两位两位分开方案,然后每一次随即交换两个位置,如果不合法的连续三个人变少了,就保留;如果不合法的连续三人数量不变就以一定的概率保留;其余情况不保留。
发现跑起来很快。
D2T3 树据结构
记 \(Q(u)\) 为 query(u)
的答案。
首先考虑一条链怎么处理。
假设我们随机一个选择一个点,然后将 \(1\) 和 \(Q(u)\) 交换,\(2\) 和 \(Q(u)\) 交换……以此类推,直到要交换的边比 \(Q(u)\) 大为止。
这样 \(u\) 的左侧和右侧的边权从 \(u\) 处就被隔断掉了,操作次数就是 \(u\) 左侧的数的数量,期望是 \(O(n)\) 的。
然后再随机一个点,先将 \(Q(v)\) 和之前的 \(Q(u)\) 比较,用和 \(Q(u)\) 一致的方式交换到不能交换为止,而交换的期望次数是 \(O(\dfrac{n}{2})\) 的。
以此类推,插入第 \(i\) 个点时交换的期望次数是 \(O(\dfrac{n}{i})\) 次。
那么我们确定所有点的步数就是 \(O(n)+O(\dfrac{n}{2})+O(\dfrac{n}{3})\dots=O(n\log n)\)。
考虑上面做法的问题在于第 \(i\) 次的期望次数是 \(O(\dfrac{n}{i})\) 的,如果我们能够把这个期望次数维持在 \(O(1)\) 的状态,那么我们就可以用 \(O(n)\) 步确定所有的点和边了。
假设我们考虑链上的 \(D\) 条边和 \(C\) 个点,那么单次操作期望次数就是 \(O(\dfrac{D}{C})\)。
这启发我们使用倍增。具体的,我们第 \(k\) 轮操作只考虑点 \(1,2\dots 2^k\) 和权值 \(\ge n-2^k\) 的边。
我们依次用之前的方法加入 \(2^k+1,2^k+2\dots 2^{k+1}\) 这些点,这样单次期望次数就是 \(O(1)\) 的;然后我们重新将整个点序列扫描一遍,确定所有 \(\ge n-2^{k+1}\) 的边。
这样的期望操作次数就是 \(O(n)\) 的了。但是有的时候倍增不一定是最优的,所以可能需要调整为一个参数 \(\alpha\)。
现在考虑如何解决树:
考虑前 \(i\) 个点构成的虚树 \(T_i\),如果我们让其中的边权覆盖 \(1,2\dots |T_i|-1\),那么我们可以通过询问 \(Q(u)\) 得到 \(u\) 是否属于 \(T_i\)。由此我们可以再使用最开始确定链的方法找到 \(T_{i+1}\setminus T_i\)。那么这棵树就被剖分成了若干个 \(T_{i+1}\setminus T_i\),然后对于每一条链处理即可。