CF1098F Ж-function

蒟蒻·廖子阳發表於2024-04-02

紀念一下第一道獨立切的 \(\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}\) 陣列的限制,即:

\[Ж(l,r)=\sum\limits_{i=1}^{\text{rk}_l-1}\left([l\le \text{sa}_i\le r]\cdot \min\left\{\min\limits_{j=i+1}^{\text{rk}_l}\text{height}_j,r-l+1\right\}\right)+\sum\limits_{i=\text{rk}_l+1}^{n}\left([l\le \text{sa}_i\le r]\cdot \min\left\{\min\limits_{j=\text{rk}_l+1}^{n}\text{height}_j,r-l+1\right\}\right)+(r-l+1) \]

前兩類情況長得很相似,我們只研究 \(\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)\) 的貢獻就是:

\[\sum\limits_{j=M+1}^{p-1}([l\le \text{sa}_j\le r]\cdot\min\{\text{mn},r-\text{sa}_j+1\})+\sum\limits_{j=p}^R([l\le \text{sa}_j\le r]\cdot\min\{\text{pre}_j,r-\text{sa}_j+1\}) \]

由於 \(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

相關文章