2025.6.26题单 做题小记
CF1515H
初步考虑和简化
有关二进制操作的数据结构问题,用动态开点 \(\text{Trie}\) 来维护。
在 \(\text{Trie}\) 树上,XOR 的标记是好维护的,然而 AND 和 OR 的操作相对冲突,考虑只保留其中一种。
注意到,\(x\) AND \(y\) 容易表示成 \(\sim((\sim x)\text{ OR }(\sim y))\),其中取反符号 \(\sim\) 也可以用 XOR \(2^{20}-1\) 表示。所以现在只需要实现 OR 和 XOR 就行了。
具体做法
考虑每次将区间 \([l,r]\) 给 \(\text{split}\) 出来,处理完后再合并回去。
我们将在每个 \(\text{Trie}\) 的节点上维护以下标记:
- 结构类:\(lc_p,rc_p\) 表示,\(p\) 节点的左/右子节点
- 贡献类:\(cnt_p\) 表示,\(p\) 节点的子树中的颜色个数(用于算答案)
- \(\text{tag}\) 类:\(tg_p\) 表示,\(p\) 节点的子树中,哪些位是要取反的
每次 XOR 操作直接对 \([l,r]\) 区间整个打取反 \(x\) 的标记。
每次 OR 操作需要将左儿子的信息合并到右儿子上。如果暴力往下找要合并的节点,单次复杂度会退化成 \(O(\text{整棵树的大小})\)。更加具体的,如果子树大小在遍历后没有变化,那么这次遍历的时间复杂度贡献就是不对的。
对上述假做法的修正
实际上,假若子树中没有要合并的点了(即不存在一个同时有左右儿子的点),则可以通过打反转 \(\text{tag}\) 实现整颗子树 OR 修改的效果。
考虑再维护一些标记来确保子树中存在要合并的点:
- 辅助类:\(t0_p\) 表示,\(p\) 节点子树中,是父节点左儿子的那些二进制位
- 辅助类:\(t1_p\) 表示,\(p\) 节点子树中,是父节点右儿子的那些二进制位
考虑要是 \(x\) AND \(t0_p\) AND \(t1_p\) 不为 \(0\),则三者 AND 后的那个最高位必定存在要合并的节点,那么我们就可以往下递归;反之就此停止。
复杂度
空间复杂度 \(O(n\log V)\),瓶颈在于 \(\text{split}\) 操作新建的 \(O(n\log V)\) 个节点。
时间复杂度 \(O(n\log^2 V)\),瓶颈在于 \(\text{OR}\) 操作的时候。每合并掉一个节点需要单次 \(O(\log V)\),至多 \(O(n\log V)\) 个节点,故而总复杂度就是二者相乘。
洛谷 P9528
直接 \(\text{DP}\) 难以优化,也难以看出来有什么良好的性质。
尝试刻画路径。单看整条路径,规律不那么显然。考虑归纳决策后进行考虑。
考虑一些简单的拐弯数少的情况。现在要从 \((i,x)\to(j,y)\),同时记 \(i,j\) 两行中间的行号为 \(k\)(即行编号从小到大为 \(i,k,j\))。
容易写出各种方法的贡献式(这里仅列举 \(3\times 2\) 的情况,\(2\times 3\) 同理):
-
\((i,x)\to(i,y)\to(j,y)\)
花费:\((y-x)A_i+(j-i)B_y\)
-
\((i,x)\to(j,x)\to(j,y)\)
花费:\((j-i)B_x+(y-x)A_j\)
-
\((i,x)\to(k,x)\to(k,y)\to(j,y)\)
花费:\((k-i)B_x+(y-x)A_k+(j-k)B_y\)
考虑我们什么时候会拐弯,也就是方案 \(3\)。尝试对比贡献式——整理可得,方案 \(3\) 比前两种更优的充要条件是:
假若需要上式满足,则 \(A\) 中必须要求:
注意到,这个形式意味着,所有 \(A\) 中的转折点(类似 \(i,k,j\) 这种),形成一个下凸壳。所以我们需要对于 \(A,B\) 两数组,求出各自的下凸壳。
然后根据上述,第一步走 \((i,x)\to(k,x)\) 而不是 \((i,x)\to(i,y)\) 当且仅当:
所以只需要每次比较斜率,贪心的合并 \(A,B\) 两个下凸壳,顺便统计一下答案即可。