題目鏈接: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;
}