[TK] BLO

初步分析

此题描述让我们想到Tarjan求割点,因此我们从割点的角度来探讨一下这道题.

假如我们去掉的不是一个割点,那么它实际上不会对连通性造成影响,但是根据样例可以看出来,删去这个点导致了其余点与当前点无法联通,即答案为 \(2\ (n-1)\).

假如我们去掉的点是一个割点,那么它必然会将整张图分成 \(k\) 部分,我们设第 \(i\) 部分的节点数是 \(s_{i}\),那么显然 部分 \(i\) 与其他部分中的每一个节点都不连通, 假如现在在部分 \(i\) 内有一个节点 \(A\),与它联通的节点数量等于 \(i\) 内其他节点数量,即 \(s_{i}-1\),又已知当前节点总数为 \(n-1\),那么与它不连通的节点数量即为 \((n-1)-(s_{i}-1)=n-s_{i}\). \(i\) 内所有点都满足这一点,因此 \(i\) 内的不连通节点对数 \(C_{i}\) 必然满足

\[\frac{1}{2} C_{i}=s_{i}\ (n-s_{i}) \]

再加上删去 \(i\) 节点导致其他点与 \(i\) 不连通,对于全部不连通的节点数量 \(C\) ,有

\[\frac{1}{2} C=(n-1)+\sum^{i}_{1 \le i \le k} s_{i}\ (n-s_{i}) \]

但是有一个问题,即通过上述代码得到的结果有部分数据偏小,说明我们还有未考虑到的部分.

来看一组样例

点击查看
5 5
1 2
2 3
1 3
3 4
4 5

输出:
8
8
8
8
8

答案:

8
8
16
14
8

image

根据我们的算法,删掉 \(3\) 之后,分出来两个 \(s=2\) 的连通块,但输出中间变量法发现,统计的 \(s\) 分别为 \(5,4,3,2,1\). 这说明从 \(3\) 返回 \(1\) 失败了.

根据数据思考,发现数据有可能出现 搜索到的连通块的 \(low\) 值小于搜索点的 \(dfn\) 的情况,譬如整颗搜索树的根节点在这个连通块上. 这样的情况我们就不会去计算它的 \(size\),那么我们需要考虑计算出这些连通块的贡献.

易得这些节点的数量等于总数量减去已搜索的节点数量,而已搜索的节点数量我们已经求出,正好是 \(1+ \sum^{i}_{1\le i\le k} s_{i}\). 它们中的每一个节点与其他连通块均不联通,即单个贡献为 \(1+\sum^{i}_{1\le i\le k} s_{i}\). 由此,有

\[\frac{1}{2} C=(n-1)+\sum^{i}_{1 \le i \le k} s_{i}\ (n-s_{i})\ + [n-(1+\sum^{i}_{1\le i\le k} s_{i}]\ (1+ \sum^{i}_{1\le i\le k} s_{i}) \]

因此,本题的实质是使用Tarjan求割点并维护子树的 \(size\).

思路实现

注意到我们在上述推论中提到的子树是指 当前点的邻边拓展得到的树,因此考虑在Tarjan时直接求出上述 \(size\).

  1. 一个节点的 \(size\) 等于其子树之和.
  2. 一个节点的子节点全部遍历完成后,该节点的根节点再计入 \(size\). (这其实与Tarjan的思想相一致).

同时,仿照树形DP,为了防止DFS的时候直接走回父节点,我们给Tarjan引入一个新参数 \(root\) 表示当前节点来处的父节点 ,当遍历到 \(root\) 时直接跳过.

我们引入一个变量 \(res\) 用于累计目前搜索到的当前节点的子节点的 \(size\) 总和. \(s_{i}\) 表示当前结束递归的子节点的 \(size\),那么我们在每次遇到满足条件的点时,累计当前 \(s_{i}\times res\) 的值到 \(ans\) ,这样就可以求出全部 \(\sum^{i}_{1 \le i \le k} s_{i}\ (n-s_{i})\) 的值. 请读者稍加思考,这样可以快速求出答案,因为它保证每对点仅会乘到一次.

代码实现

点击查看
void tarjan(long long s,long long root){
    dfn[s]=low[s]=++cnt;
	long long son=0;
	long long res=0;
    for(int i:e[s]){
		if(i==root){
			continue; //防止走回去
		}
        if(!dfn[i]){
            tarjan(i,s);
			size[s]+=size[i];
			low[s]=min(low[s],low[i]);
            if(dfn[s]<=low[i]){
                son++;
                if(root!=0||son>1){
                    cutpoint[s]=true;
					ans[s]+=(long long)size[i]*res; //积累s[i]*res
                    res+=size[i];
                }
            }
        }
        else{
			low[s]=min(low[s],dfn[i]);
		}
    }
    ans[s]+=(long long)((n-res-1)*res); //积累特判
    size[s]++;
}
int main(){
	long long m;
	cin>>n>>m;
	for(long long i=1;i<=m;++i){
		long long x,y;
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	for(long long i=1;i<=n;++i){
		if(!dfn[i]){
			tarjan(i,0);
		}
	}
	for(long long i=1;i<=n;++i){
		cout<<2*(ans[i]+n-1)<<endl; //加上刚才没加的
	}
}
posted @ 2024-03-11 16:15  HaneDaniko  阅读(16)  评论(0编辑  收藏  举报