CF587F Duff is Mad
这题如果往根号方面想还是比较容易想到做法的。另外这题可以不带 log 的!
首先,区间内的串在某个串的出现次数,可以考虑广义 SAM 或者 ACAM。感觉两个东西都可以做这个题,为了复习 ACAM (为了去做上次 edu 的G)写下 ACAM 吧。
我们考虑 sl…r 在 sk 里出现次数这个问题。可以看成 对 sx,x∈[l,r] 看它在 sk 的出现次数。这个问题显然是可以前缀和的,也就是 s1…r 在 sk 的出现次数减去 s1…l−1 的出现次数。于是我们可以把 sk 给提出来。设总长度是 L ,这个时候我们就有了两种做法:
- 一个一个字母加入 s1…n ,我们设当前加入字母后在 AC 自动机上的节点 u ,我们考虑它是 sk 所处的一连串节点的多少个节点的祖先。如果它是某个节点的祖先,那么就说明在这个节点的位置在 sk 中出现了一次。我们可以预处理所有 sk 的节点的祖先的位置的贡献系数,复杂度是 O(L) 的。
- 直接给 1…n 的字符串一个一个把字符串结尾处的子树 + 1,当处理到一个位置,只需要 O(lensk) 跑一下询问串看看这个位置被加了多少次。这样做对于每个询问串的复杂度是 O(len) 的,总共还有个复杂度也就是一个一个 + 1的复杂度。我们可以用 BIT 轻松做到 O(nlogn) ,但是这样查询复杂度也变成了 O(lenlogn) 。又可以那根号平衡来优化,加一的时候 O(√L) ,查询的时候 O(1) 。于是处理一个串的复杂度是 O(len) 的,总复杂度是 O(qlen+n√L) 。
可以把两个方法合并一下,假设大于 M 的串我们用第一个方法,否则用第二个方法,于是复杂度是O(L2M+qM+n√n) 可以假设 q,L 同阶,把 M 设到 √L 复杂度就有了 O(L√L+n√L)
cpp
1 |
|