1. 程式人生 > 實用技巧 >Blockade(tarjan求割點...)-poi2008

Blockade(tarjan求割點...)-poi2008

Blockade(tarjan求割點...)-poi2008

題意:n個點,m條邊雙向連通,無重邊,自環;輸出n個數,代表把第i個點去掉後,有多少訪問不能發生a->b ≠b->a;

解:對於一個點有兩種情況:

1,非割點:結果(n-1)*2,這個點不能到別的店,別的點也不能到這個點;

2,割點。

判斷一個點是否為割點

1,對於根節點,計算其子樹數量,如果有兩顆以上的子樹,就是割點,去掉這個點,兩顆子樹不會互達;

2,對於非根節點,tarjan求。維護兩個陣列dfn[],low[],dfn[u]時間戳,low[u]頂點u及其子樹中的點,通過非父子邊,能夠回溯到的最早的點(dfn最小)。

對於邊(u,v),如果dfn【u】<=low【v】,u就是割點,其他類似tarjan求連通分量

割點狀況:

1》,以u為根的子樹對外界的來往

設子樹數量為ss,對外界n-1+n-1

2》,外界對以u為根子樹的來往

(n-1-ss)*(ss+1)其他點到這顆子樹上的點

即:ans【u】+=(n-1-ss)*(ss+1)+(n-1)

/*size[u]表示以u為根的子樹有多少節點

isok[u],判斷u是否為割點*/

vector存圖會超記憶體,用鏈式前向星存圖

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10; const int mod=142857; const int inf=0x3f3f3f3f; typedef long long ll; typedef pair<int,int> pii; const int N=5e5+10; int n,m; struct edge { int v , next ; }e[N<<1] ; int tot,last[N] ; inline void add(int u,int v) { e[++tot]=(edge){v,last[u]}; last[u]=tot ; e[
++tot]=(edge){u,last[v]}; last[v]=tot ; } ll ans[maxn]; int idx,dfn[maxn],low[maxn]; int size[maxn]; bool isok[maxn]; void tarjan(int u) { dfn[u]=low[u]=++idx; size[u]=1; int cnt=0,ss=0; for ( int i = last[u] ; i != -1 ; i = e[i].next ) { int v = e[i].v ; if (!dfn[v]) { tarjan( v ) ; size[u] += size[v] ; low[u] = min ( low[u] , low[v] ) ; if ( dfn[u] <= low[v] ) { cnt ++ ; ss += size[v] ; ans[u] += (ll)size[v] * ( n - size[v] ) ; if ( u != 1 || cnt > 1 ) isok[u] = 1 ; } } else low[u] = min ( low[u] , dfn[v] ) ; } if(!isok[u]) ans[u]=(n-1)*2; else ans[u]+= (ll)(n-ss-1)*(ss+1)+(n-1); } int main() { while(cin>>n>>m) { memset(last,-1,sizeof(last)); memset(isok,0,sizeof(isok)); for(int i=1; i<=n; i++) dfn[i]=low[i]=0; tot=idx=0; for(int i=1; i<=m; i++) { int u,v; cin>>u>>v; add(u,v); } tarjan(1); for(int i=1; i<=n; i++) printf("%lld\n",ans[i]); } }