『联合省选2025集训』『Trie 树,K-D 树,线段树』Day1 略解

前言

且视他人之疑目如盏盏鬼火,大胆地去走你的夜路。

先做的前天的 DP 套 DP,可能浪费了一个上午在麻将这个题上,感觉今天的题做不完了。

A

只有我这种小丑会不知道 \(\texttt{Trie}\) 树怎么维护全局加 \(1\) 了吧,笑死。

显然遇到这种,就是把儿子的异或和通过父亲维护出来即可。

唯一的麻烦在于怎么对于一个点维护的异或和全局加 \(1\)

考虑每个点维护他的儿子的二进制串的 \(\texttt{Trie}\) 树,对于树上的每个对应的二进制串,加 \(1\) 其实就是将二进制位最低的 \(0\) 变为 \(1\)\(0\) 前面的 \(1\) 全部变成 \(0\)

故考虑从低到高位建 \(\texttt{Trie}\) 树,每次全局加 \(1\),其实就是先递归那个表示二进制位 \(1\) 的儿子,然后交换 \(0,1\) 所表示的儿子(因为一个要变成 \(1\),一个要变成 \(0\)),惊奇的发现这样最多只会走下去一条链,单次操作时间复杂度 \(\mathcal{O}(\log V)\)

至于异或和怎么维护,其实本质上就是 \(\texttt{Trie}\) 树上每层深度对应的节点的点权为 \(1\) 的数量的奇偶性。

其他的感觉就是在加减的时候有一个删除原来的数加入新的数的操作的细节处理,没什么讲头。

第一次知道 \(\texttt{Trie}\) 树可以维护全局加 \(1\),是不是废了。

\(\texttt{Code}\)

B

TZL 说很简单,我信了,然后想了 40 多分钟都没搞明白简单在哪里。

然后他又说随便找规律,然后完全不知道怎么找。

简称太菜了。

我们发现,先建完一颗 \(\texttt{01 Trie}\) 树,那么我们每次选择一个点对应的就是这个点的子树和其到根节点路径上所有的点都不能选择了。

发现输入完之后,能选的点会构成若干棵满二叉树。

由于是博弈,考虑从 \(\text{SG}\) 函数入手,求出深度为 \(i\) 的满二叉树的 \(\text{SG}\) 值。

考虑观察一下我们是如何操作的:

  • 删掉根节点,那么这棵树直接没了。

  • 删掉一个深度为 \(1\) 的节点,剩下一颗深度为 \(i-1\) 的满二叉树。

  • 删掉一个深度为 \(2\) 的节点,剩下一颗深度为 \(i-1\) 和一颗 \(i-2\) 的二叉树。

  • 以此类推,删掉一个深度为 \(i\) 的节点,剩下深度为 \(i-1,i-2,.....,1\) 的二叉树。

简而言之,\(\text{SG}(i)=\text{mex}(\text{mex}_{j=1}^i (\oplus_{k=j}^{i-1} \space \text{SG}(k)),0)\)

然后由于 \(L\) 太大,考虑简单的对每个 \(i\)\(\text{SG}\) 打个表,发现 \(\text{SG}(i)=\text{lowbit}(i)\)(这里指的不是二进制位,\(\text{lowbit}(4)=4\)。), 然后直接对于输入完后构成的每个满二叉树的 \(\text{SG}\) 异或起来正常博弈论判断就做完了。

\(\texttt{Code}\)

C

很难,先咕咕咕。

D

随手猜个结论,\(k\) 的答案肯定是被经过路径数量最多的点。证明的话就看怎么构造出来的就行了。

打完了,有 \(\texttt{15 pts}\),考虑怎么去构造方案。

摆烂了,只会 \(\mathcal{O}(n\times m)\) 的,我是彩笔。

考虑一种构造方法,每次一次性的填完一种颜色。

题解中的话很清晰:如果能选出若干两两不交的路径,经过所有的关键点,那么重复进行 \(k\) 次该过程即可构造出方案。(关键点的定义是被路径经过次数最多的点)

问题就在于怎么去选择两两不交的路径。

然后依然不会,我真的只有截图一下题解了:

简而言之,就是每次选择一个时间戳最小的,且是关键点的点 \(x\),然后选择一条 \(\text{lca}(a,b)=x\) 的路径,然后把这条路径所占用的点全部扣掉然后给路径涂色,然后在剩下的点中继续寻找关键点(注意这个关键点被经过的路径次数不只是剩下的最多的,还要和 \(x\) 的次数相同)然后一直递归删路径然后涂色,直到找不到位置。

此时复杂度应该是 \(\mathcal{O}(n\times m)\)

考虑加速删点和寻找关键点的过程,因为路径通过树链剖分之后会分成 \(\log n\) 个区间,把这些区间直接扣掉(具体可以在线段树上区间减),在剩下的 \(\log n\) 个区间中寻找出现次数最多的点,如果这个点的出现次数和 \(x\) 相同,那么递归,否则就不进入这些区间递归。

时间复杂度 \(\mathcal{O}((n+m)\times \log^2n)\)

常数巨大,极限 \(\text{1908 ms}\) 卡过。

\(\texttt{Code}\)

E

算是对于 \(\texttt{K-D tree}\) 的简单应用吧。

首先看到只出现一次,找到这个数上次出现的位置 \(\text{pre}_i\),下一个出现的位置 \(\text{nxt}_i\),那么 \(i\) 在区间 \([l,r]\) 只出现一次的充要条件就是 \(i\in [l,r], \text{pre}_i\in [0,l-1], \text{nxt}_i\in [r+1,n+1]\)

三维偏序,找到满足条件的 \((i,\text{pre}_i,\text{nxt}_i,a_i)\) 的点对的 \(a_i\) 的最大值。

直接暴力看作三维区间查询,上 \(\texttt{K-D tree}\) 即可。

反正没被卡就是过了。

\(\texttt{Code}\)

F

\(\texttt{K-D tree}\) 的比较基础的运用。

先考虑平面最远点对怎么做。

本质上就是通过该数据结构找到每个点距离最远的点取一个 \(\max\)

具体来说,维护一个当前最大值 \(\text{ans}\),本质上寻找过程就是在 \(\texttt{K-D tree}\) 上递归寻找,最慢的肯定是要把每个点剩下的 \(\mathcal{O}(n)\) 都拿来求一遍距离然后取最大的。

故我们考虑结合 \(\texttt{K-D tree}\) 的性质进行剪枝。

  • 首先是一个类似估价函数的东西,就是说你先找到当前节点所管辖的矩阵距离“你要询问最远距离的点”的最长距离。如果你发现这个理论最长距离已经比你当前求出来的答案要小了,那么这个节点管辖矩阵里的所有点显然都是没用的。

  • 故我们考虑对于数据结构上的每个点所管辖的矩阵按照理论最长距离弄出估价函数,那么在递归的时候,我们先进估价函数大的,再进小的,如果当前节点的估价函数比答案小,那就不用继续做了,直接返回。

然后对于第 \(k\) 远,考虑打一个堆,把前 \(k\) 远的全部放进去,按照类似平面最近点对的方式,仍然按照同样的估价函数估价,如果估价比堆顶小且堆的大小已经有 \(k\) 了,那么显然这个节点也是没有用的。

注意,这样计算的点对是有序的,\(k\) 要乘 \(2\)

\(\texttt{Code}\)

posted @ 2024-12-23 12:02  Saltyfish6  阅读(9)  评论(0编辑  收藏  举报
Document