Désive

求单个区间答案可以用一个 Trie 上的 dp 实现:从根往下考虑,如果左右子树都不是满的,那么就是左右子树答案的 \(\max\);否则为左右子树答案的 \(\max\) 加上 \(2^i\)。

对于这个 dp 更好的刻画是,选择一个叶子,那么这个叶子对答案的影响是到根路径上所有满的兄弟子树大小之和。扫描线,维护每个左端点的答案,考虑新加入一个叶子的影响。若 \(a\) 是排列,则每个叶子只会被加入一次,如果这次加入使得一个子树从不满变成满,则对兄弟子树的所有叶子重构对答案的影响。总的重构次数是 \(O(2^nn)\),更新一个叶子对答案的影响相当于关于到根路径上满的兄弟子树内叶子被点亮的最早时间的分段函数,朴素实现会有 \(O(2^nn^2)\) 次数据结构上的操作。

诶然后我们发现当点亮一整个子树 \(x\) 重构兄弟子树 \(y\) 的叶子时,单个叶子对答案的影响可以拆分为 \(y\) 内部和 \(y\) 外部两部分。\(y\) 外部对于所有叶子都是一致的,而 \(y\) 内部的影响不超过子树大小。于是我们对于 \(y\) 内部所有取值处理出最大的左端点,然后依次加上外部的影响,这样只有 \(O(2^nn)\) 次数据结构上的操作(upd:好像分析错了,还是 \(O(2^nn^2)\),如果想去掉一个 \(n\) 的话需要和祖先归并而非依次修改)。

回到 \(a\) 不是排列的原问题。现在一个子树可能会重构很多次。考虑重构一个叶子时,需要关心的是每个子树所有叶子最后一次出现位置的最小值。然后扫描线往右移时只会更新一个叶子的最后一次出现位置,也就是说子树最小值可能会有一个 \(v\to v'\) 的变化,显然只需要重构左端点在 \((v,v']\) 时的答案。对于每个子树,我们都使用数据结构维护之前做法中【内部每个取值对应的最大左端点】,然后找出修改之后会变化的左端点,加上外部影响后修改答案,同时修改数据结构上的左端点。

上面这个想法看上去过于简单了,对这个数据结构的形容过于抽象。事实上若对偶地去看暴力做法中的数据结构,可以发现和原问题极强的关联性。不去对于每个取值维护最大左端点,而是对于每个左端点维护最大取值,这样连续段数量只有子树大小,同时只需归并上祖先的影响,诶那我们发现这样的话和原问题是一样的。同时根据上文还可以注意到,假设扫描线移动时使得 \(x\) 这个子树作出了 \(v\to v'\) 的变化,则对于其兄弟子树 \(y\),只需要关心左端点 \(\in(v,v']\),因为 \(\leq v\) 的左端点必定在之前就加入过贡献,而 \(>v'\) 的左端点取值不会超过 \(v'\) 处。更重要的事情是 \(v\to v'\) 是增加的,而取值随左端点增大而减小,因此只需要支持删除和查询最大值就可以去掉关于时间的限制!直接套用原问题一开始的做法,也就是说对于原 Trie 上的每个子树 \(p\),新建一棵 Trie \(p'\),叶子上维护一个时间标记代表最后一次出现时间,支持更新一个叶子,删除最后一次出现最早的叶子,查询 xormex。当 \(x\) 发生 \(v\to v'\) 的变化时,找到兄弟子树 \(y\) 对应的新 Trie \(y'\),求出 xormex 以及对应的左端点 \(t\),和祖先归并贡献,紧接着删去出现时间最早的叶子。同时需要在 \(O(n)\) 棵 Trie 上进行修改一个叶子的操作。插入次数是 \(O(2^nn)\),查询和删除同样。加上祖先贡献时采用归并的方法,这样在数据结构上操作次数也是 \(O(2^nn)\) 的。

\(o=1\) 时数据结构只需要支持前缀 chkmax,查询单点值。\(o=2\) 时需要支持区间历史和,颜色段均摊一下变成前缀赋值区间历史和,这个颜色段均摊如果我没想错的话应该确实是在线段树上的修改次数不变,但换成区间就错了,因为遍历到一个中间的颜色段时可能并不会删掉它,前缀就没有这个问题了。时间复杂度 \(O(2^nn^2)\)。

难点应该在于找到如何将 \(a\) 是排列时的 \(O(2^nn^3)\) 做法优化至 \(O(2^nn^2)\),以及将这个做法利用势能推广至一般情况。

posted @ 2026-10-17 20:33  BYR_KKK  阅读(28)  评论(0)    收藏  举报