ZROI 2023.12.24 T2
很硬的题目!
题意
给出一棵 \(n\) 个点的树以及它以 \(1\) 为根时的一种 DFS 序,\(q\) 组询问(强制在线):给定 \(k\) 个区间 \([l_1,r_1],[l_2,r_2]\dots[l_k,r_k]\),问 DFS 序在这些区间内的点构成几个连通块。
80 分解法
对 \(k\) 根号分治,\(k>\sqrt{n}\) 直接暴力,\(k\le\sqrt{n}\) 的考虑一种 \(\Theta(k^2)\) 做法。连通块数等于点数减边数,所以我们对于每个区间内的点考虑它的父亲是不是在给定的这些区间内,则可以差分成 \(\Theta(k^2)\) 个形如 区间 \([l,r]\) 内有多少对父子关系 的询问,这个考虑扫描线扫儿子,使用 \(\Theta(\sqrt{n})-\Theta(1)\) 的分块维护父亲 DFS 序。强制在线则考虑可持久化分块。由于时间 2s,空间 1024MB,可以通过 \(n\le 2\times 10^5\) 的部分。
正解
和 80 分解法毫无关系。以下假设点已经按照 DFS 序标号。
对某个区间 \([l_j,r_j]\),从 \(l_j\) 开始,设 \(nxt_u\) 表示 DFS 序上 \(u\) 后面第一个 \(u\) 子树外的点,那么每次跳 \(nxt\),直到当前点的 DFS 序大于 \(r_j\),跳的次数就是连通块数。所以单个区间可以用倍增维护这个来算。
接下来考虑区间之间的影响。DFS 序区间在原树上形成的结构形如从 \(fa_{l_j}\) 到 \(LCA(fa_{l_j},r_j)\) 的一条链上的所有点,它们所有 DFS 序大于等于 \(l_j\) 的子树都被标记(\(LCA(fa_{l_j},r_j)\) 的部分子树除外)。我们需要考虑在 \(j\) 之前的其它区间是否覆盖了这条链上的某个点,如果覆盖了,那么就要减少 被标记的儿子个数 这么多个连通块。考虑如何统计这个。
注意力非常集中的人可以发现,由于所有区间不交,随着 \(l_j\) 不断变大,某一些区间会变得没有用。具体地,每个 DFS 序区间都会覆盖上述链上的一段区间中的点,假如它与上述链交非空,且不包含链端点即 \(LCA(fa_{l_j},r_j)\),那么在以后的询问中,它一定没有用了。于是基于这一点,我们用一个栈维护所有区间,每次暴力弹栈,用倍增等手段维护链上区间内 被标记的儿子个数 的和。总复杂度是 \(\Theta(n\log n)\),实在是非常牛的。当然花花说可以用更复杂的精细实现做到整个题完全的线性,正常人类不会关心这一点。