【題目描述】
Byteotia城市有n個 towns m條雙向roads. 每條 road 連接 兩個不同的 towns ,沒有重復的road. 所有towns連通。
Input
輸入n<=100000 m<=500000及m條邊
Output
輸出n個數,代表如果把第i個點去掉,將有多少對點不能互通。
Sample Input
5 5
1 2
2 3
1 3
3 4
4 5
Sample Output
8
8
16
14
8
【題目分析】
題目讓求刪去每個點以后會形成多少不能聯通的城市對。
我們很容易聯想到強連通分量求割點,割點肯定會形成很多個不能連通的城市對,而不是割點的話除了自身就沒有其他不能連通的。(仔細研究樣例就能發現自身和其他城市也算在內)
可是問題是對于每一個割點怎么來求多少個城市呢?
首先:割點上不同子樹之間肯定無法連通
其次:割點的子樹和除割點外的其他點無法聯通
最后:對于每個被刪除的點都和其他點無法聯通
需要注意的是每個點對都要計算兩次(這也是研究樣例得到的)
最后一個很簡單,就是(n-1)*2(我們后面的討論都不討論乘2,最后統一乘起來就是)
其次中的我們只需要統計每個割點的所有子樹上總共有多少個點tmp,那么(n-1-tmp)*tmp就是答案
重點來看對于首先應該怎樣解決。我們不妨現想一下如何統計每個割點的子樹的節點,肯定是在搜索子樹后再回溯得到每一棵子樹的節點的個數,然后加起來。對于當前這個子樹(節點數num[v]),他和前面所有的子樹節點(節點數tmp=num[v1]+num[v2]+…+num[vi-1])都不連通,所以我們讓答案加上num[v]和前面子樹節點和的乘積,再更新子樹節點和就好(詳見代碼Trajan函數部分)
【AC代碼】
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;
const int MAXM=5e5+5;
struct node
{int v,next;
}Edge[MAXM<<1];
int head[MAXN],tot;
int DFN[MAXN],LOW[MAXN];
//bool cnt[MAXN];
int vis[MAXN],Time;
ll ans[MAXN],n,m;
ll num[MAXN];void init()
{memset(head,0,sizeof(head));tot=0;memset(vis,0,sizeof(vis));Time=0;//memset(cnt,0,sizeof(cnt));memset(ans,0,sizeof(ans));memset(num,0,sizeof(num));
}void AddEdge(int u,int v)
{tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;
}void read()
{int u,v;for(int i=0;i<m;i++){scanf("%d%d",&u,&v);AddEdge(u,v); AddEdge(v,u);}
}void Trajan(int x,int fa)
{int v; ll tmp=0;int son=0;DFN[x]=LOW[x]=++Time;vis[x]=1; num[x]=1;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(vis[v]==0){Trajan(v,x);num[x]+=num[v];son++;LOW[x]=min(LOW[x],LOW[v]);//if(x==root &&son>1 || x!=root && LOW[v]>=DFN[x])if(LOW[v]>=DFN[x]) //這里不需要判斷是否是根節點,因為如果如果是根節點只有一個子樹的話tmp為0,ans[x]為0,要是有多個子樹就是割點,直接計算。{ans[x]+=tmp*num[v];tmp+=num[v];//cnt[x]=true;}}else if(vis[v]==1){LOW[x]=min(LOW[x],DFN[v]);}}vis[x]==2;ans[x]+=tmp*(n-tmp-1);
}void solve()
{for(int i=1;i<=n;i++){if(!vis[i]){Trajan(i,i);}}
}void print()
{for(int i=1;i<=n;i++){printf("%lld\n",2ll*(ans[i]+n-1));}
}int main()
{while(~scanf("%lld%lld",&n,&m)){init();read();solve();print();}return 0;
}