Address
https://www.lydsy.com/JudgeOnline/problem.php?id=5329
Solution
首先介绍一下圆方树。
先求点双连通分量,设原图中的点为圆点。
然后对于每个点双连通分量建一个方点,从方点向连通分量内的所有点连一条边,并把这个连通分量里原有的边删掉。
在 Tarjan 求点双的过程中,所有点分别入栈了一次,而除了根之外所有的点都出栈了一次,设有
个点双,那么除了出栈过的
个点进入点双之外,每次找到连通分量时还要把一个割点算入,这样所有点双的点数之和为
,而上面的构图中圆点和方点的总数为
,所以得出,这样得到的是一棵树,这棵树就是圆方树。
圆方树有一些神奇的性质:
(1)原图的割点对应圆方树的割点(方点除外),也就是属于多个方点的圆点。
(2)两个点
之间的割点集合为圆方树
到
的路径上除
和
的所有圆点。
回到问题。建出圆方树,可以把问题进行转化。
求一个点集之间两两的路径的并上,有多少个圆点(这个选出的点集除外)。
点集之间两两的路径并实际上就是这个点集构成的虚树。
把关键点按 DFS 序排序后我们要求的就是求相邻两个点之间路径的并。
我们给每条边一个权值:
边,如果深度较大的点
为圆点则
的权值为
,否则为
。
这样,问题再次转化:
求点集
所在虚树的边权和,减去
,如果虚树的根为圆点则加一。
容易想到,点集
的虚树的边权和,
就是 DFS 序排序之后
到
,
到
,…,
到
,
到
的路径边权之和除以
。
利用倍增 LCA 即可求得。
Code
cpp