描述
Byteotia 城市有 n 个城镇,m 条双向道路。每条道路连接两个不同的城镇,没有重复的道路,所有城镇连通。
输出 n 个数,代表如果把第 i 个点去掉,将有多少对点不能互通。
输入
输入 n,m 及 m 条边(n≤10^5,m≤5×10^5 )。
输出
输出 n 个数,代表如果把第 i 个点去掉,将有多少对点不能互通。
样例输入
5 5
1 2
2 3
1 3
3 4
4 5
样例输出
8
8
16
14
8
思路(tarjan)
题目有规律:如果去除的不是割点,答案为2*(n-1),如果是割点,则是各个子集大小乘以剩余集合的大小,再求和,加上n-1
所以重点是记录各个子集的大小,因为与生成树不一样,这里有回路,所以应该用两个参数记录
【注意】
去掉某一点后产生的点对主要有三种:
1、u点和其他的点(n-1)
2、u点下的各个子树内的点与其他的点(不含u)
3、u点下的所有子树外的点(可以理解为父亲方向上的子树,但包含于u点同双联通的点)到其他的点
【代码存在的遗留问题】
对于 sum[u]+=(n-res-1)*res+(n-1)
不知道为什么这里要(n-res-1),这个 -1 就很奇怪,但是答案对了
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10,INF=0x3f3f3f3f;
int n,ans,m,t,idx;
vector<int>vec[N];
int dfn[N]={0},low[N]={0},s[N]={0};
ll sum[N]={0};
void tarjan(int u,int pre){
low[u]=dfn[u]=++idx;
s[u]=1;//存子树节点数量
int num=0;
ll res=0;//res已用块的节点总数
for(int i=0;i<vec[u].size();++i){
int ne=vec[u][i];
if(!dfn[ne]){
num++;
tarjan(ne,u);
low[u]=min(low[u],low[ne]);
s[u]+=s[ne];//记录u的子树节点数量
if((low[ne]>=dfn[u]&&pre!=u)||(u==pre&&num>=2)){
//x的子树ne能提供的个数 * 前面也就提供的同属x子树的个数和
sum[u]+=s[ne]*res;//所有子树的边
res+=s[ne];//加上ne的节点数
}
}
else
low[u]=min(low[u],dfn[ne]);
}
//不知道为什么这里要(n-res-1),这个-1就很奇怪,但是答案对了
sum[u]+=(n-res-1)*res+(n-1);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i,i);
for(int i=1;i<=n;++i){
if(i>1) printf("\n");
printf("%lld",sum[i]*2);
}
return 0;
}