紀念一下第一道獨立切的 \(\color{maroon}*3500\)。
不過這種萌萌套路題是怎麼 \(\color{maroon}*3500\) 的?雖然第一次見的時候感覺比較厲害,但這題是我第四次見這個套路,就覺得很板了。
值得注意的是,本題解中的做法空間是線性的。
在閱讀本題解之前,請確保你會以下演算法:
- 線性 / \(1\log\) / \(2\log\) 字尾陣列
- 序列分治 / cdq 分治
- \(1\log\) 二維偏序
畢竟這是一道 \(\color{maroon}*3500\) 的題目,所以主要講思路,一些較為基礎的東西以及實現細節就不細說了。
題目傳送門
給出長度為 \(n\) 的字串 \(s\)。定義 \(Ж(l,r)=\sum\limits_{i=l}^r|\text{lcp}(s[l,r],s[i,r])|\)。\(q\) 次詢問,每次給出 \(l,r\),查詢 \(Ж(l,r)\)。
\(n,q\le 2\times 10^5\),\(\text{6 s / 500 MB}\)。
先字尾排序。
將子串的 \(\text{lcp}\) 搞成字尾的 \(\text{lcp}\),則 \(Ж(l,r)=\sum\limits_{i=l}^r\min\{|\text{lcp}(s[l,n],s[i,n])|,r-l+1\}\)。
然後將詢問掛在 \(\text{rk}_{l}\) 上,分別計算 \(\text{rk}_i<\text{rk}_l\) 和 \(\text{rk}_i>\text{rk}_l\) 的貢獻,最後算上 \(l\) 本身的貢獻。此時可以將 \(\text{lcp}\) 的限制轉化成 \(\text{height}\) 陣列的限制,即:
前兩類情況長得很相似,我們只研究 \(\text{rk}_i<\text{rk}_l\) 的情況。
對 \(\text{height}\) 陣列進行序列分治(其實此處比較像 cdq 分治),記當前分治區間為 \([L,R]\),中點 \(M=\dfrac{L+R}{2}\),\(N=R-L+1\)。考慮當前層右半邊對左半邊的貢獻。
記 \(\text{pre}_j=\min\limits_{k=M+1}^R\text{height}_k\)。對於左半邊按 \(M\rightarrow L\) 的順序掃描 \(i\),並同時記錄 \(\text{mn}=\min\limits_{k=i+1}^M\text{height}_j\)。
考慮掛在 \(i\) 上的一個詢問 \((l,r)\)。
此時,存在 \(p\in[M+1,R]\) 使得當 \(j\in[M+1,p)\) 時 \(\text{mn}\le \text{pre}_j\);當 \(j\in[p,R]\) 時 \(\text{mn}>\text{pre}_j\)。
化簡第二層 \(\min\{\}\),那麼右半邊對 \((l,r)\) 的貢獻就是:
由於 \(i\) 遞減,\(\text{mn}\) 不升,因此 \(p\) 不降,最多遞增 \(\mathcal{O}(N)\) 次。
然後將 \(\min\{\}\) 拆開,即討論一下誰是最小值,此處我們只討論前面那個數更小(不等於)的情況。因為兩種討論都是類似的。
對於 \(j\in[M+1,p)\) 的部分,我們要求 \(\sum\limits_{j=M+1}^{p-1}([l\le \text{sa}_j\le r\land \text{mn}<r-\text{sa}_j+1]\cdot \text{mn})\),提取公因式 \(\text{mn}\) 後發現是關於 \(j,\text{sa}_j\) 的二維偏序。可以用樹狀陣列維護 \(\text{sa}_j\),在 \(p\) 移動時更新樹狀陣列(就是掃描線)。
對於 \(j\in[p,R]\) 的部分,我們要求 \(\sum\limits_{j=p}^R([l\le \text{sa}_j\le r\land \text{pre}_j<r-\text{sa}_j+1]\cdot \text{pre}_j)\)。
接下來是重點,也是這個套路最巧妙的一步。
如果按照之前的方法找偏序關係,發現是關於 \(j,\text{sa}_j,\text{sa}_j+\text{pre}_j\) 的三維偏序。你要是在分治內部再套個樹套樹 / cdq 分治的話複雜度肯定爆炸。
我們先求 \(\sum\limits_{j=p}^R([l\le \text{sa}_j\le r\land \text{mn}<r-\text{sa}_j+1]\cdot \text{pre}_j)\)。這東西拆開後是二維偏序,樹狀陣列類似維護。
由於右半邊 \(\text{pre}_j<\text{mn}\),漏算的貢獻是 \(\sum\limits_{j=p}^R([l\le \text{sa}_j\le r\land \text{pre}_j<r-\text{sa}_j+1\le \text{mn}]\cdot \text{pre}_j)\)。你發現這還是個三維偏序,那不是白搞?別急,你發現當 \(j\in[M+1,p)\) 時,\(\text{mn}\le \text{pre}_j\),即 \(\text{pre}_j<r-\text{sa}_j+1\le \text{mn}\) 不成立。因此直接忽略掉 \(j\) 這一維限制即可!那麼剩下的就是關於 \(\text{sa}_j,\text{sa}_j+\text{pre}_j\) 的二維偏序,由於掃描的是 \(p\),所以離線下來再樹狀陣列維護即可。
那麼這種情況就討論完了。剩下的一種情況是類似的,尤其是對於 \([p,R]\) 這部分貢獻三維偏序轉二維偏序的時候,都是將 \(\text{mn}\) 代入二維偏序,再加上 \((\text{pre}_j,\text{mn}]\) 漏算的 / 減去 \((\text{pre}_j,\text{mn}]\) 多算的,然後透過不同區間 \(\text{pre}_j,\text{mn}\) 大小關係忽略 \(j\) 那一維限制。
至於 \(\text{rk}_i>\text{rk}_l\) 的情況,只是需要再分治的時候換成掃描右半邊,對左半邊維護字尾最小值,計算貢獻部分經過瞪眼觀察或手推後都可以發現是一模一樣的。
那麼這題就做完了。
可以發現一層分治的時間複雜度為 \(\mathcal{O}\left(\left(N+\sum \limits_{i=L}^RQ_i\right)\log n\right)\)。考慮到分治樹的深度為 \(\mathcal{O}(\log n)\),且對於同一深度的區間而言 \(\sum N=n,\sum Q_i=q\)。所以總的時間複雜度為 \(\mathcal{O}\left((n+q)\log ^2 n\right)\),空間複雜度為 \(\mathcal{O}(n+q)\)。常數較大,但是目前洛谷最優解第三。實現細節看程式碼吧。
AC Link & Code