解題報告-論對“區間可持久化”的新理解

KarmaticEnding發表於2024-12-05

解題報告-論對“區間可持久化”的新理解

當我第一眼看到“可持久化 \(\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}\),一個知道但是沒有形成意識,一個是根本不知道,所以這道題我認為是好的。

相關文章