2022-12-21 #17 还是无法面对 逃跑留下的拙劣的剪影
——不鱼《中间灰》
同样地,因为昨天基本没做题,并入今天的闲话。
呜呜呜,真的没有看博客的老哥愿意评论一下吗。(难道阅读量都是 bot 刷的吗)
昨天入手了人格解体,真的是非常优秀的作品,很喜欢!



打完 USACO Pt 组了,感觉比去年(指 USACO22FEB)的简单一些。(去年只会做半道 T1,菜啊)
随便写一下 USACO 题解吧:
铜 T1:签到。
铜 T2:不会!随便猜了个结论过了!(贪心往后放)
铜 T3:不会!随便猜了个结论过了!每次暴力枚举一个合法的切分位置并操作。
银 T1:不会!随便猜了个结论过了!dfs 一下树,把要移动的位置之间的边建出来拓扑排序。
银 T2:不会!随便打了个表过了!可以发现每个位置相对独立,而一个位置胜利的条件是模 \(4\) 不为 \(0\),最小步数分模四余数讨论一下,为 \(0,2\) 每一步都会取 \(2\),\(1,3\) 一定是取与其余数相同的最大素数。
银 T3:把相同的段缩在一起,而我们又知道相邻差值的绝对值,也很容易判断相邻两个差值的符号正负性是否相同,就做完了。
金 T1:比较喜欢这种题。如果确定了哪些奶牛参加,我们一定是按照 \(X\) 从小到大喂冰淇淋。那么将所有奶牛将 \(X\) 排序后,找到最后一个喂冰淇淋的奶牛,其前面邀请的奶牛一定只喂冰淇淋,后面邀请的奶牛一定只给钱,因此前缀后缀分别做个背包就好了。
金 T2:直接 \(O(n^2\log n)\) 搞过去的。枚举点对靠左的点 \(p\),计算出它到右边所有点的斜率,那么其合法右端点一定是非严格前缀最大值。由于只有单点加操作,我们可以用线段树暴力找到后面不再合法的位置并删掉。(我感觉维护笛卡尔树结构可以均摊,把 \(\log\) 去掉,但没细想)
金 T3:每次删掉最小度数的点,然后把这个过程时间倒流,并查集维护连通块大小就好了。
铂金 T2:可以发现操作过程形成了若干个团的结构,初始我们将每条边看成一个大小为二的团,而删去一个点不过是将其相邻所有边的团合并,启发式合并就好了,两个 \(\log\)。(听 hzr 讲能 \(1\log\),膜拜 hzr)
84 USACO22DEC BREAKDOWN P
本场最难的题。
不妨讨论 \(k=8\) 的情况,其余情况做法是一致的。
首先有个显然的时光倒流,变加边。
折半,我们在外层枚举折半点 \(k\),分别维护 \(1\) 到 \(k\) 与 \(k\) 到 \(n\) 经过 \(4\) 条边的最短距离。
两种情况是是一致的,不妨考虑 \(1\) 到 \(k\) 的距离怎么维护。我们再次折半,对于每个点,我们维护 \(1\) 到 \(x\) 与 \(x\) 到 \(k\) 经过 \(2\) 条边的最短距离,同样只考虑第一类距离如何维护:
加入一条边 \((x,y)\),我们讨论其作为第一条边还是第二条边。若为第一条边,这类边只有 \(n\) 条,我们枚举每个点作为第二条边的中点,并更新距离;若为第二条边,直接更新当前边终点的距离就好了。
每个 \(k\) 的复杂度都是 \(O(n^2)\),总复杂度 \(O(n^3)\)。
当时以为代码会很难写,写完才发现只写了 2k。
85 USACO22DEC PALINDROMES P
感觉是我比较擅长的类型。
首先考虑一个序列怎么应该怎么调整:(合法条件是 \(0\) 数量为偶数或 \(1\) 数量为偶数)
相同的数之间显然不会 swap,于是我们可以将字符配对,配对了的字符在最后会在对称的位置上。若序列长度为奇数,就先把和自己配对的位置移到中间,那么此时左边的 \(0,1\) 数量均达到了序列长度的一半,因此两边不会继续进行交换。(序列长度为偶数则不然)
一个贪心的想法,我们从内到外,依次把配对的数对向外挪,具体地,令第 \(i\) 对数对距离中间的距离为 \(a_i,b_i\),其移动后的距离为 \(f_i=\max(f_{i-1}+1,\max(a_i,b_i))\)。
(其实我这里的距离定义比较奇怪,当序列长度为偶数时距离为 \(d,\cdots,2,1,1,2,\cdots,d\),奇数时中间的 \(1\) 只有一个)
事实上上面的贪心也是正确的,于是我们得到了一个 \(O(n^3)\) 的做法。
加一比较难受,由于 \(f_i\geqslant i\),我们套路地将 \(f_i\) 减去 \(i\),得到 \(f_i'=\max(f'_{i-1},\max(a_i,b_i)-i)\)。
那么答案就是:
直接地枚举区间太劣了,有两个优化枚举的想法:枚举序列中点,每次向外扩展一个字符;枚举最终序列的中点(若长度为偶数,则枚举最中间的两个 \(0\)),每次向外扩展一对相同的字符。(假设当前两端为 \([l,r]\),下次变为 \([l',r']\),那么我们事实上在枚举左端点在 \([l'+1,l]\),右端点在 \([r,r'-1]\) 的区间)
第一个方式经过尝试,并不太能优化。
第二个方式的优点在于,每次只会在尾端 push 一对新位置进来,不会改变其他位置对的顺序,而上面的前缀 \(\max\) 恰好可以利用这个性质。
但是序列的中点位置会改变,我们将上面的贡献写成一个带中点位置 \(x\) 的函数:
此时尾端 push 的优势就出来了,我们可以直接计算出新位置的 \(a',b'\),并不会改变其他位置对的值,很容易用树状数组维护出函数对于每个 \(x\) 的值,复杂度 \(O(n^2\log n)\),听说可以卡过去,但优化非常简单。
在枚举的过程中,\(x\) 的变化量是不超过上面的枚举量的,因此我们可以直接维护一个指针,查询的时候类似莫队暴力加、删贡献,复杂度 \(O(n^2)\)。
86 uoj#750. 【UNR #6】小火车
当时看了遍 sol 忘记写代码了,补一下。
显然可以转化成,选择两个集合,使得这两个集合和相同。
根据鸽巢原理,一共有 \(2^n\) 个球,\(p\) 个盒子一定有盒子不止一个球。事实上我们可以类似二分找到这个盒子,每次把区间劈成两半,一定有一半的球总数大于盒子总数,而计算总和在 \([l,r]\) 内的集合数量可以用折半在 \(O(2^n\log n)\) 内预处理,\(O(2^n)\) 查询。
复杂度 \(O(2^n\log p)\)。
下面是今天的内容。
草,怎么一个上午就要过去了,我好像还没做什么题啊。
87 CF1286E Fedya the Potter Strikes Back
比较简单,可是不但没做出来,还被搞自闭了,怎么回事(
显然我们只要考虑计算当前所有 border 的 \(\min\) 之和就好了。
我们尝试维护当前的 border 集合,以及当前每个 \(w\) 值贡献了多少次答案。
虽然 border 数量很多,但被删除的 border 数量是均摊 \(O(n)\) 的,我们尝试快速找到一个被删除的 border。
我们定义一个 border 的颜色为 border 后面一个字符,在 fail 树上维护与自己颜色不同的第一个祖先,于是我们就可以快速枚举被删除的 border 祖先。(枚举到,且没有被删除的结点数量少于被删除的结点数量,因此冗余枚举不会影响复杂度)
而 \(\min\) 值的更新只需暴力将所有贡献大于 \(w_i\) 的位置暴力改为 \(w_i\),用个 map 维护就好了。(同样可以均摊,只会改 \(O(n)\) 次)
复杂度 \(O(n\log n)\)。
vp:CF1767,状态不太好,感觉自己打的特别下饭,但 perf 还不错的样子?
88 CF1767F Two Subtrees
其实这东西比较板,但我之前没写过(
我们现在有一个操作序列,若从左到右扫一遍所有操作,指针移动次数总和较小,我们可以把莫队的分块过程带权(令一个位置所在块为其对应前缀指针移动次数除以阈值 \(B\)),可以类似莫队将复杂度平衡。(在 P6580 [Ynoi2019] 美好的每一天~ 不连续的存在 中使用过)
对于这题,由于只有 \(n\) 个区间,我们想把这些区间以某种方式排成一排,使得从左到右扫一遍这些区间,指针移动次数总和较小,继而应用带权莫队。
一个很类似的问题是 dsu on tree,我们不妨考虑一种 dfs 序,轻儿子在前面,然后是重儿子,然后是当前结点,使用 dsu on tree 的复杂度分析可知指针移动次数是 \(O(n\log n)\) 的。
注意由于要用 dsu on tree 的分析,莫队有时候不能暴力移动,而是把当前区间一个个删除,然后再一个个地加入目标区间。
复杂度 \(O(n\sqrt q\log n)\)。
所以为啥我常数这么大啊(
89 infoj199 Dynamic Predecessor Problem
参考 《浅谈亚 log 数据结构在 OI 中的应用》 - 学习笔记 。
其实就是复习一下压位 trie。
我们建立一棵 \(w\) 叉树,二进制压位每个儿子是否有合法值,使用二进制操作可以 \(O(1)\) 寻找前驱后继。
单点修改,前驱,后继操作,复杂度均为 \(O(\log_w V)\) 复杂度。
若值域较小,我们可以做到 \(O(\frac Vw)\) 的空间复杂度(最底层叶子不用建),但动态开点就只能 \(O(n)\) 空间复杂度。
(上述操作均可以令 \(w=64\))
如果要支持求 rank 呢?
我们肯定不能插入完就暴力更新每个结点的每个儿子的元素个数前缀和,不妨定期重构。
一棵子树在插入 \(w\) 次后重构,这样重构的复杂度保证了,考虑如何计算零散元素。一个暴力的方法:用 \(1\) 的个数表示一棵子树零散元素的个数,用 \(0\) 分割每棵子树,只需查询一个二进制数第 \(k\) 个 \(0\) 的位置。
令 \(w=O(\log n)\),暴力预处理上面那个东西就好了,复杂度 \(O(\frac{n\log V}{\log\log n})\)。
好拉啊,有没有好一点的方法(
实现可以类似 zkw 线段树,自下向上地建立。
见提交记录。
vEB 树有点难写,会压位 trie 应该够了(
vEB 树的思想是折半,一个值域 \(n\) 的 vEB 树是 \(O(\sqrt n)\) 叉的,此外再用一棵 vEB 维护每个儿子信息并的子问题。
这样每次对值域取根号,就可以做到 \(O(\log\log V)\) 的复杂度。

浙公网安备 33010602011771号