BZOJ1123 [POI2008]BLO (Tarjan)
阿新 • • 發佈:2018-12-17
題目
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;
}