1 条题解
-
0
自动搬运
来自洛谷,原作者为

iostream
Think three times, code twice and take the first place.搬运于
2025-08-24 22:17:57,当前版本为作者最后更新于2020-05-21 23:27:55,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目描述
给定一个长度为 的字符串 ,有 次询问,每次给定两个参数 , ,询问子串 所构成的后缀树的结点数。
题解
tag:分类计数;后缀树/后缀自动机;线段树/树状数组;哈希。
做法来自2018集训队论文集。
由于构建后缀树需要倒转原串之后建立 SAM 得到,为方便处理,故我们先倒转原串以及对应询问区间。
下文例子的字符串都是倒转之后的。
记 表示找区间 对应子串在后缀树上的节点。
考虑传统后缀树节点数的计数方法,对于一个区间 建后缀自动机的过程,产生出了 np 和 nq 两种类型的节点。
np 节点对应的是 的前缀 对应的串,暂时称作前缀节点;
nq 节点对应的是后缀树上必要的分叉,暂时称为分裂节点;
example:"pabcqabc"
对于 "abc" 这个串,子树分成 "pabc" 和 "qabc" 两个独立的节点,故 "abc" 本身新建了一个分裂型节点。
考虑容斥,计算前缀节点个数 + 分裂节点个数 - 前缀&分裂节点的个数。
前缀节点个数
可以直接得到,即 。
分裂节点个数
放到后缀树上考虑,依然以上串 "pabcqabc" 为例:
"abc" 这个串在树上产生分叉(你考虑后缀树中实际相当于插入了 "cbap" 跟 "cbaqcbap" 俩串)的必要条件是其在当前区间中出现 次,且存在两个出现位置,其往前一个位置的字符不同。
预处理整个串的后缀自动机以及树,离线询问。
考虑按右端点扫描线,扫到 的时候,将 对应的字符串 标记上 这个位置。
现在等价于求有多少个节点 ,当前存在两个不同子树中的 、 使得 且 ,那么 对应的节点将在 中作为分裂节点出现。
这个是个传统树上数据结构题,一种好写的方法是 lct 维护后缀树,access 更新过程中从 到 的路径上染上 这种颜色,再维护每个节点来自不同子树最后两次染上的颜色分别是 和 ,然后再用树状数组维护树上所有节点的 ,这样查询树状数组上 的部分的和就是分裂节点的个数。
这一段时间复杂度是 ,来自于 lct 和树状数组。
接下来还需要计算前缀&分裂节点的个数。
考虑一种可行的暴力,枚举所有 找到 的位置 (※),然后再判断该位置是否有两个或以上子树,然后再判断是否 ,仅当全部为“是”就对答案带来 的贡献。
(※)笔者注:这个位置不一定恰好是树上的点,可能在边上,在边上显然不满足有两个或以上子树。这个位置就是对应的前缀节点将产生的位置,也就意味着,对 这个区间建后缀树,所有的分裂节点一定是整个长串的后缀树上节点的子集,而 个前缀节点不一定全是。
接下来将该暴力优化至 的复杂度:
引理:所有可行的 是一段前缀。
证明:显然一个串 能找到两个出现位置,其往前一个位置的字符不同,则 也可以。
example:"abcdabceabc"
其中 "abc" 作为分裂&前缀节点出现,可以直接说明前三个前缀节点均算重了。
有了引理,就可以上一个二分,每次 check 这个串是否形成分裂节点即可。
在后缀自动机上找 ,传统方法是利用倍增预处理,单次以 的复杂度查询;
判定分裂节点可以直接用上一部分算出的 来直接判断;
该算法复杂度为 。
但是这玩意还可以进一步优化,注意到 是分裂节点的必要条件是 有两个或更多子树。
后缀树上有两个或更多子树的串只有 个,可以考虑用字符串哈希预处理其对应的节点。
二分的时候如果在哈希表中难以找到 这个串,就说明该串在树边上而非节点上,一定不会成为分裂节点。
那么只需要二分找到最大的 ,容斥掉的点数即为 。
该算法复杂度为 。
需要代码可以去这里查看。
- 1
信息
- ID
- 5177
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者