省选集训 day 1 数据结构杂题
A
比较套路的题目,第一次见还是有难度的。
关于 \(+1\) 的更改,事实上是找到二进制下极长的末尾 \(1\) 段并进位。
考虑使用 Trie 维护这个操作,相当于建立一颗从低位开始的 Trie,然后swap儿子并进入swap后的新左子树递归操作。
然后对于邻域的问题,一般考虑每个点单独维护其儿子,然后特别处理父亲。
B
发现对于把串插入 Trie 后每个 Trie 极大空子树独立,也就是相当于若干个 \(L\) 的空串的公平组合游戏。
那么答案就是这些空子树的 SG 值的 \(xor\)。
考虑 \(SG(n)\) 的求法,这里表示树高为 \(n\) 的空 Trie 树,注意是树高,那么 \(n=0\) 指的是空树。
首先 \(SG(0)=0\),因为此时已经必败。
其次考虑先手做一次可以达到一个什么样的局面:插入一个长为 \(h\) 的串,原树被分割为 \(n-h,n-h+1,n-h+2\dots n-1\) 这样的子问题。
那么也就是 \(SG(n)=\operatorname{mex}\lbrace \bigoplus_{j=n-h}^{n-1}SG(j)|0\le h\le n\rbrace\)
注意到 \(h=0\) 时取到零。
打表发现 \(SG(n)=\operatorname{lowbit}(n)\)
C
考虑 xor mex 如何求解,显然可以考虑一个 \(dp\),设 \(f_{i}\) 为 Trie 上节点 \(i\) 的最大 \(xormex\),有:
考虑到一个方法自底向上求解最优值,也就是对于 一颗满树 \(T\),给其Trie上的兄弟子树的权值加上 \(|T|\),然后找到权值最大的叶子即为答案。
通过这个方法,我们可以预处理每个 \(r\),\(O(n)\) 个 \((l,h)\),表示 \(ql\le l\) 时,往上走 \(h\) 级的树满了,然后通过 Trie 打标记的方法(因为这些是统一的)在 \(O(2^n·n·\sqrt m)\) 回答询问。
这太差了,考虑到树 \(T\) 满上之后,事实上 \(T\) 内点不可能成为答案,并且我们只关心 \(T\) 的兄弟节点的最大值变化,所以可以只打一个标记在这里不用。
考虑 \(a\) 是排列的情况,那么每个子树只会被填满一次,那么可以扫描线,扫到 \(r\) 的时候对于若干对 \((l,h)\) 我可以暴力访问其兄弟子树里所有节点更新答案并在这 \(n\) 对 \((l,h)\) 中 \(l\) 划分的 \(O(n)\) 个区间将答案取 \(\max\)。
一共下来只会有 \(O(n·2^n)\) 次操作,利用线段树求算答案也就是 \(O(n^22^n)\)
考虑到 \(a\) 不是排列的情况如何转化,我们的核心思想还是在于对于每个 \(r\),求出若干对 \((l,w)\) 表示 \(ql\le l\) 时答案对 \(w\) 取 \(\max\)。
首先我们断言这样的 \((l,r,w)\) 总数是 \(O(n·2^n)\) 级别的。
还是考虑扫描线,每次考虑 \((a_r,r)\) 的影响,其中对于每个子树,使用一个 Trie 来维护这个子树的节点,我们称这样的trie为内trie,而原本的trie为外trie(也即有 \(O(2^n)\) 个内 Trie,总结点数 \(O(n2^n)\)),并且维护信息:当前点所在子树全满的最大 \(l\) 以及这个内 trie 的 dp 值。
首先将 \((a_r,r)\) 插入到外trie祖先的内trie里,我们可以得到这个 \(r\) 所对应的不超过 \(n\) 个的 \((l_i,h_i)\)。
一个 \((l_i,h_i)\) 对答案的影响是什么呢?其实可以考虑其兄弟子树的最大值变化,因为我们实际上也只关心其兄弟子树的最大值。
对于每个子树维护一个集合 \(P_i\) 表示当前的内trie内节点,按照加入时间排序(也就是下标)。然后遍历 \(P\) 内 \(\le l_i\) 的节点逐个删除,包括在集合P删除以及在内trie上删除(这个点以后完全无用了,下次更新不会用到(已满)),并获得新的该子树的 \(dp\) 值作为这个时间段的答案。
注意我们是加入 \((a_r,r)\) 时,也将 \(a_r\) 的所有外 trie 上的祖先的 \(P\) 都加入它。
这样均摊下来所有的 \(P\) 的总插入量是 \(O(n·2^n)\),总删除量不超过插入量。
但是注意到这个兄弟子树的答案还有一些情况,也就是从这个兄弟子树往上走祖先的时候,如果祖先的兄弟也满了,那么这个答案也是会更新的,但是这是增量。
至于 \(a_r\) 对应的单点,就只需要考虑祖先的兄弟满上的时刻。
需要在相应时间处理一遍。
注意到上面的复杂度有个瓶颈,就是往上走祖先找满了的祖先兄弟的增量。
这个卡满每次会有 \(O(n)\) 个,会导致 \(O(n^2·2^n)\) 个要求的 \((l,r,w)\),因此我们需要想办法优化掉这些。
事实上我们发现所谓的 “祖先兄弟” 仅有 \(O(n)\) 个,我们虽然处理出与其相关的权值,但是我们每个 “祖先兄弟” 取其权值的最优值,那么也就只有 \(O(n·2^n)\) 个。
同时为了处理 “兄弟子树” 的 “祖先兄弟”,我们先拿出所有祖先兄弟,然后将其按时间排序,并且用指针扫描(避免排序)处理贡献。
这样就可以做到严格的 \(O(n·2^n)\) 了。
由此我们得到了 \(O(n·2^n)\) 个二元组 \((l,r,w)\) 表示扫描线到 \(r\) 时,\(ql\le l\) 的答案对 \(w\) 取 \(\max\)。
由于答案具有单调性,我们可以在线段树上二分查找到更新的区间,那么我们需要的就是:支持区间覆盖,区间历史和求和。
线段树维护即可。
D
猜测结论是最大覆盖次数,因为这个是一个显然的下界。
事实上确实如此,只要我们能够证明这个下界可以达到。
考虑一个构造:每次将覆盖次数最大的点拿出,并拿出其中深度最小的点,这样的点一定会有其作为 \(lca\) 的路径,如果没有,那么它的父亲也是覆盖次数最大的点,与其深度最小矛盾。
使用全局平衡二叉树/树链剖分支持链修改,查全局最值即可。
E
预处理每个元素的极长有效区间 \((l,i,r)\)
也就是询问:\(L,R\) 这个三元组有贡献需要有 \(L\in [l,i],R\in [i,r]\)
也就是 \(l\in [1,L],r\in [R,n],i\in [l,r]\),将这个三元组看作点,变为三位数点,上 KDT 即可。
F
非正解
利用 kdt 做 K 近邻搜索,注意这玩意的 估价函数设理论最优值,然后注意使用整体操作,也就是始终对 ans 进行操作(也就是并不是对于每个点找 \(k\) 个,而是维护一个答案集合,如果可能更新就去搜),使用可并堆即可。

浙公网安备 33010602011771号