CF587F Duff is Mad
题意
给定 \(n\) 个字符串 \(S_{1...n}\).
定义 \(\text{occur}(t, s)\) 为 字符串 \(t\) 在字符串 \(s\) 中的出现次数. 有 \(q\) 次询问,每次给出 \(l\),\(r\) 和 \(k\),输出 \(\sum\limits_{l\le i\le r}\text{occur}(s_i, s_k)\).
\(n,k,\sum |s_i|\le 10^5\)
题解
我们对所有串建立fail树
令 \(p_{i,j}\) 表示串 \(s_i\) 的前 \(j\) 个字符在AC自动机上对应点的编号,\(end_{i}\) 表示串 \(s_i\) 在AC自动机上对应点的编号
有
\[\sum_{l\le i\le r}\text{occur}(s_i, s_k)=\sum_{l\le i\le r}\sum_{1\le j\le \text{len}_k}\text{isanc}(end_{i}, p_{k, j})
\]
其中 \(\text{isanc}(x, y)\) 表示在fail树上 \(x\) 是否为 \(y\) 的祖先
看到 \(\sum |s_i|\le 10^5\) 这条限制,容易想到对于 \(|s_k|\) 根号分治
令 \(M=\sum |s_i|\)
- \(|s_k|<=T\)
容易得到一种做法,\(\forall l\le i\le r\),对所有以 \(end_i\) 为根的子树的所有点加1,然后查询所有 \(p_{k,i}\) 点上的值之和
把每个询问差分为成 \([1, l-1]\) 和 \([1, r]\) 两个区间,然后离线下来按右端点排序,维护区间修改单点查询
树状数组可以做到 \(\mathcal{O}(qT\log M+n\log M)\),当然更快的做法是分块的 \(\mathcal{O}(qT+n\sqrt{M})\) - \(|s_k|>T\)
满足这个条件的 \(k\) 数量是 \(\mathcal{O}(\frac{M}{T})\) 级别的,因此我们考虑对所有的 \(k\) 预处理
考虑另一种做法,把所有 \(p_{k,i}\) 标上1,然后查询 \(\forall l\le i\le r\),\(end_i\) 子树和之和
预处理时我们对 \(end_i\) 的子树和做前缀和,询问的差分即可
\(\mathcal{O}(q+\frac{M}{T}\times n)\)
选择一个合适的 \(T\),我们视 \(n\),\(M\) 和 \(q\) 为同阶,\(T\) 取 \(\sqrt{M}\) 即可
复杂度可以简单地认为是 \(\mathcal{O}(n\sqrt{M})\)
不忘初心 砥砺前行