P9052 题解
我们考虑如何处理一对询问
对于当前选择的根,如果删掉点
需要注意的是,根可能支配自己。根的所有出度如果都会被自己删掉那么他们在支配树上的 LCA 就会支配根。所以实际上,我们最后得到的关系是一个支配基环树。对于环上的每个点,我们如果删掉他就会让这个基环树被删掉;对于不在环上的点,删掉它会使得子树里的点被删掉。
可以发现,我们可以把一个图变成基环树森林再统计答案。考虑按
可以发现
- 如果当前这个点
u 的出度都在自己这颗树里,把他们的 LCA 往u 连一条边就好了。 - 如果所有出度都在另一颗树中,同样求出 LCA,我们现在就是是把
u 的子树接到 LCA 下面。 - 如果出度分散在不同的联通块,此时
u 并不任何点支配。但是随着合并的进行,可能在某一刻,u 的出边全都在某一个联通块里了。所以我们在加入u 的时候往每个联通块里扔一个标记,每次合并两个联通块的时候把两个联通块都有的标记的计数器减一,减到0 就需要把u 重新拿出来,把出度的 LCA 和它连边。我们可以考虑用线段树合并维护这个过程就可以做到单 log。每次在 merge 两个叶子的时候处理一下计数器。开一个队列记录当前需要考虑的u ,每次拿队首出来做。计数器变成0 就 push 进去。
考虑怎么算答案,即对于每个时刻的支配森林求出有多少个点对
答案就是每个点的 siz 和也就是 dep 和,我们要维护的操作是把一个树根接到某个点的儿子处。考虑在 LCT 上维护 Link,求某个点的 dep,求 LCA,求联通块 siz 的操作就可以做了。
如果过程中出现了一个新的基环树,需要特殊处理一下。由于每个联通块只会变成一次基环树,我们可以直接暴力的通过某些手段
但是要注意的是,一颗基环树虽然不会再往外连出出度,但是里面的点之后有可能作为其他的点的出度。为了方便后续的答案计算,需要把基环树上的环点的 dep 人为的修改一下。具体的,把根的 dep 改为环长,其他环点的 dep 改成
以及,每颗树的树根有可能编号大于当前枚举的
总体的时间复杂度为
另一个做法,我们可以先直接求出
首先应该怎么求出
我们考虑不断的缩图。考虑一个点
维护缩图的过程可以使用线段树合并,可以发现和第一个做法中维护的信息大同小异,每个联通块的根处维护有那些点连向他,每次合并的时候相当于把两者都有的点的出度减一,每次找到出度变为
之后我们考虑怎么求出每个
之后考虑统计答案。我们拎一个环上的点当根搞一颗外向树。这个图会存在至多一条返祖边。我们把答案分为直上直下和必须经过返祖边的两部分进行统计。
- 对于直上直下的部分,可以并查集维护 siz 和最浅的那个点,很好做。
- 对于经过返祖边的部分,我们可以巧妙地把最开始钦定的根设置成使得那个返祖边是环上边权最大的边的那个点。这样我们就能保证在加入返祖边的时候,整个环就完整的形成了。
- 加入返祖边之前只有直上直下的贡献。
- 加入返祖边时,由于只会加入一次,我们可以暴力的 DFS 一遍算贡献。
- 加入返祖边后,可以发现如果一对
(u,v) 满足从u 走到v 必然要经过返祖边那么一定是形如u 处于v 在环上的投影以下一直到返祖边端点的那一条链上。于是也可以很方便通过并查集的 siz 信息计算贡献。
最后整体的时间复杂度依然是
代码(LCT做法):https://www.luogu.com.cn/paste/21uo8ogi