1. 程式人生 > >BZOJ1123 [POI2008]BLO (Tarjan)

BZOJ1123 [POI2008]BLO (Tarjan)

題目

傳送門

Description

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

題意

給出一個無向圖,刪去某個點,求有幾對點無法互相到達。

題解

先用Tarjan求出割點 我們通過畫圖可以發現,當我們刪去一個割點時,整個圖就會分裂成幾個連通塊。 這樣的話,這幾個連通塊相互之間都無法到達,所以我們構造一棵DFS樹。 這樣刪去一個點的話,就會分離成上面一棵樹還有若干棵子樹。 然後我們就可以利用每個連通塊的節點數統計ans。

注意: 1、int會爆,要開long long; 2、(x,y)和(y,x)算兩對; 3、每個刪去的點與其他的點也算,所以要加上。

具體實現見程式碼:)

Code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10,MAXM=1e6+10;
struct E{int x,y,next;
} ed[MAXM]; int last[MAXN],tot; int dfn[MAXN],low[MAXN]; ll ans[MAXN],cnt[MAXN]; int num,n; void ins(int x,int y)// 建邊 { ed[++tot]=(E){x,y,last[x]}; last[x]=tot; } void init() // 初始化 { memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(cnt,0,sizeof(cnt)); memset(ans,0,sizeof(ans)
); memset(last,0,sizeof(last)); tot=num=0; } void Tarjan(int x) { dfn[x]=low[x]=++num; cnt[x]=1; ll bef=0;//bef表示x之上的節點數 for(int k=last[x];k;k=ed[k].next) { int y=ed[k].y; if(!dfn[y]) { Tarjan(y); cnt[x]+=cnt[y];// cnt用於表示以該點為root的子樹的節點數 low[x]=min(low[x],low[y]); if(dfn[x]<=low[y])//當x為割點時,將這可子樹分成幾塊 { ans[x]+=bef*cnt[y]; // 每一次找到他的子樹時都加上以他為root的子樹的節點數乘上他上面的節點數 bef+=cnt[y];//維護bef的值,使其始終為x之上的節點數 } } else low[x]=min(low[x],dfn[y]); } ans[x]+=bef*(n-bef-1);//此時的bef表示這棵子樹的節點數目,再乘上其他子樹的節點數即可 } int main() { int m,x,y; init(); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); ins(x,y); ins(y,x); } Tarjan(1); for(int i=1;i<=n;i++) printf("%lld\n",(ans[i]+n-1)*2);// 最後要加上i到其他n-1個點的情況 // 最後答案要乘上2,因為是雙向的(x,y)和(y,x)是兩種不同的情況 return 0; }