解题报告-论对“区间可持久化”的新理解

解题报告-论对“区间可持久化”的新理解

当我第一眼看到“可持久化 \(\texttt{Trie}\)”的时候,我以为这不过是一个 \(\texttt{Trie}\)+可持久化罢了。事实证明也是这样,在后面的代码实现中,我也是一遍打对了这个紫色板子。

那么,一道模板题,有什么好说的?正是因为控住我的不是模板,这道题才得以上升至好题之列。

题意很清楚,但是一开始我甚至没有想到这怎么跟可持久化 \(\texttt{Trie}\) 联系起来。后来看到标签里面说前缀和,我以为是后缀贴上了前缀的标签,于是开始考虑后缀怎么写。想到了线段树等很多个做法,但是都假了。

卡掉我的基本都是同一个点:它是异或。如果 \(a>b\),那么不一定有 \(a \oplus c>b\oplus c\)

于是我到题解里寻求帮助。我发现了这道题中的第一个关键点:

  • 首先,它确实是一个前缀问题。如果我们直接维护后缀,那么意味着我们必须区间更新,但是前缀和相反。往序列末尾加入一个数的时候,只用更改一个值。这启示我们,一切异或问题都是可以逆向考虑的。比如记 \(s_i\) 表示 \(a_1\oplus a_2\oplus \dots \oplus a_i\),那么 \(\oplus_{i}^{n} a_i=s_{i-1}\oplus s_n\)。因为异或是可以自反的,即 \(a\oplus b=c\Rightarrow a\oplus c=b\)

那么下一步就显得略微简单了。我们已经建出了一棵 \(01-\texttt{Trie}\) 树,每次查询的时候,设查询中异或的数为 \(x\),那么 \(x\leftarrow x\oplus s_n\),然后在 \(\texttt{Trie}\) 树中找到与 \(x\) 异或最大的数。具体也很好找,就是每一位都尽量与 \(x\) 不同即可。

可持久化也很简单,每加入一个数,就更新一下,最后会生成 \(n\)\(\texttt{Trie}\)

接下来是第二大难点:如果这样做,我们只能查询 \(1\sim r\) 的区间,而非任意的 \(l\sim r\)。很显然,这里也不可能像可持久化线段树:静态区间第 \(k\)一样,用一个前缀和的思想。我们该如何判断,哪些数是合法的,能够被统计进答案,哪些数是不合法的,要被从答案里踢出去?

这里有一个几乎所有区间可持久化通用的 \(\texttt{Trick}\),就是 \(\texttt{timestamp}\)。众所周知,每次修改一条链的时候都要把链上每个点都复制一遍。在第 \(k\) 个版本的线段树里,我们记录 \(tim_i\) 表示 \(i\) 号点最晚什么时候被复制,这里 \(tim_i\le k\)。那么,统计 \([l,r]\) 答案的时候,我们从第 \(r\) 个版本的树开始找,那么这个时候树上的合法结点只有 \(tim\ge l\) 的。对于那些 \(tim\le l\) 的——看不见!就当没有这个结点。那么这道题就圆满完成了。

总结下来,这道题直接拿下两个 \(\texttt{Trick}\),一个知道但是没有形成意识,一个是根本不知道,所以这道题我认为是好的。

posted @ 2024-12-05 20:28  KarmaticEnding  阅读(42)  评论(0)    收藏  举报