题目链接:https://www.luogu.com.cn/problem/P3469
分两种情况;
情况1:选择的不是割点,说明删除该点后,整张图还是连通的,剩余 n-1 个点还是可以互相访问的,只是少了被删的点访问其他点与其他点访问被删的点,即(n-1)*2 次。
情况2:选择的是割点,删去该点后,整个图会被分成 k 个连通块,访问此时会少Σki (第i个连通块的节点数) 乘 n-ki+ n-1(别忘了被删节点访问其它节点的次数)。
我们用tarjan来判断每个点是否是割点,并再tarjan的过程中,记以每个节点为根的子树大小。如果 low[u] >= dfn[v],说明u不能通过其它路径到达v之前的点,所以v就是割点,当然如果 v 是根的话,只有他有两个不能到达v之前的子树,才是割点,本题特判一下 点1 即可。
代码如下
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
typedef long long ll;
struct node
{
int to,next;
}p[maxm];
int head[maxn],dfn[maxn],low[maxn];
ll ans[maxn];
int size[maxn];
int cnt,dfn_num;
int n,m;
bool cut[maxn];//标记是否为割点
void add(int x,int y)
{
p[++cnt].next=head[x];
p[cnt].to=y;
head[x]=cnt;
}
void tarjan(int u)
{
dfn[u]=low[u]=++dfn_num;
size[u]=1;//以该点为根的子树大小
ll sum=0;
int tag=0;//判断是否是割点
for(int i=head[u];i;i=p[i].next)
{
int v=p[i].to;
if(!dfn[v])
{
tarjan(v);
size[u]+=size[v];
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
sum+=size[v];
ans[u]+=(ll)size[v]*((ll)n-(ll)size[v]);
tag++;
if(tag>1||u!=1)
cut[u]=1;
}
}
else
low[u]=min(low[u],dfn[v]);
}
if(!cut[u])
ans[u]=(n-1)*2;
else
ans[u]+=(ll)(n-sum-1)*(ll)(sum+1)+(n-1);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}