SOJ1737 题解
题意
给定一个长度为 \(n\) 的串 \(S\)。
定义 \(occ(T)\) 表示串 \(T\) 在 \(S\) 中出现的次数。
\(q\) 次询问,每次询问给定一个区间 \([l,r]\),查询 \(S[l,r]\) 的所有子串的 \(occ\) 之和。
\(1\le n,q\le 2\times 10^5\)
题解
学到了新技能,来记一下。
这是一道看起来很典的题目。但实际上并不好做。
先思考 \(n^2\) 做法(也是我的做法)。可以想到一种转化,即求 \(\sum\limits_{i=1}^n\sum\limits_{j=l}^r\operatorname{lcp}(S[i,n],S[j,r])\)。离线后可以做到 \(O(n^2)\)。但这种做法已无法优化。
换一种,定义 \(f(T)\) 为 \(T\) 的所有后缀的 \(occ\) 之和。建出后缀树,则若我们知道 \(T\) 在 \(\text{sam}\) 的节点,可以 \(O(1)\) 求。此时原式等价于 \(\sum\limits_{i=l}^rf(S[l,i])\)。因为 \(S[l,l],S[l,l+1],\dots,S[l,r]\) 在 \(\texttt{sam}\) 上是一条链,所以这种做法是 \(O(n^2)\) 的。
“链”的性质为接下来的优化创造条件。这里引进一种新科技:\(\text{DAG}\) 链剖分。我们想要这样的性质:\(\text{DAG}\) 上任意一条路径经过 \(O(\log P)\) 条链,其中 \(P\) 为路径总量,在 \(\text{sam}\) 上是 \(n^2\)。建链的方法如下:
- 对所有节点,求以 \(i\) 为起点的路径数量 \(f_i\) 和以 \(i\) 为终点的路径数量 \(g_i\)。
- 令 \(nxt_i\) 为所有 \(i\) 出点中 \(f\) 最大的点,\(pre_i\) 为所有 \(i\) 入点中 \(g\) 最大的点。
- 若边 \((u,v)\) 满足 \(nxt_u=v\wedge pre_v=u\),则其为重边。否则为轻边。
- 此时只保留所有重边,即可完成链剖。
证明一下上面的性质。若走一条轻边,必有 \(2f_v<f_u\vee g_v>2g_u\)。于是得证。
对于建链,另一种解释是:先由 \(nxt\) 建树,再由 \(pre\) 树剖。这样可以更直观地感受 \(\text{DAG}\) 链剖的妙处。
建出链后,利用前缀和进行优化,二分哈希找到何时跳出链。复杂度 \(O(n\log^2n)\)。