CF1098F Ж-function
纪念一下第一道独立切的 \(\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-i+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\) 的情况,只是需要再分治的时候换成扫描右半边,对左半边维护后缀最小值,计算贡献部分经过瞪眼观察或手推后都可以发现是一模一样的。
那么这题就做完了。
记 \(i\) 这个位置上挂了 \(Q_i\) 个询问。可以发现一层分治的时间复杂度为 \(\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)\)。常数较大,但是目前洛谷最优解第三。实现细节看代码吧。