首先考虑怎么比较树上两个点对 \(x1,y1,x2,y2\) 中 \(str(x1,y1),str(x2,y2)\) 的大小关系,我们可以找到这两个串的 LCP 后比较下一个字符的大小关系,找两个串的 LCP 可以直接二分答案然后算出哈希值看是否相等,单次比较是 \(O({\log n}^2)\) 的。
我们使用点分治,假设当前分治中心为 \(C\),当我们计算点对 \(x,y\) 的答案时,考虑怎么计算和 \(x\) 不在 \(C\) 的同一个子树里的点的答案。对于不在一个子树里的点 \(k\),我们将 \(str(x,k)\) 分为 \(str(x,C)+str(C,k)\),那么如果 \(str(x,y)\) 中和 \(str(x,C)\) 长度相等的一段就可以分出胜负,那么发现这些点要么都可以要么都不可以,否则 \(str(x,y)\) 和 \(str(x,C)\) 的 LCP 一定超过 \(str(x,C)\) 的长度,所以我们将 \(x\) 往 \(y\) 的方向跳 \(str(x,C)\) 的长度步,假设跳到了点 \(z\),那么问题就变成了有多少个 \(str(C,k)\) 字典序小于 \(str(z,y)\),很容易想到可以建出字典树然后在上面直接每次沿着 \(str(z,y)\) 的方向往下面跳,然后统计左边有多少个点。这个东西肯定会超时,不过我们发现可以先预处理出跳到字典树上每个点时一路上左边的点数,然后二分出第一个能完整出现在字典树上的前缀,剩下的就只需要枚举小于这个前缀后面一位的所有点数,这一个过程是 \(O(\log^2+c)\) 的,其中 \(c\) 为字符集大小。
感觉这个部分说的有点抽象,画一个图感受一下。
这是一颗字典树,假设此时我们要查询有多少个串是小于 \(\texttt{bebc}\) 这个串的,然后我们会先跳到节点 \(5\) 上,因为这是最长的出现在字典树上的前缀,然后答案会加上预处理出的 \(3\),分别是串 \(\texttt{a}\) 和 \(\texttt{b}\) 以及 \(\texttt{bc}\),因为这些是小于从根到节点 \(5\) 的路径的总数,这个东西在字典树上 DFS 一遍即可预处理出。然后之后我们枚举节点 \(5\) 经过比下一个字符小的边可以到的点,然后答案就会加上 \(2\),分别是串 \(\texttt{be}\) 和 \(\texttt{bea}\),这样子就可以在 \(O(\log n^2+c)\) 的复杂度内算出答案。
最后套上点分治,总时间复杂度就是 \(O((n+q){\log n}^3)\) 的。
又臭又长的代码。