POJ2378/BZOJ3391 網絡破壞 : Tarjan割點+dfs
Description
約翰意識到貝茜建設網絡花費了他鉅額的經費,就把她解僱了.貝茜很憤怒,打算狠狠報
復.她打算破壞剛建成的約翰的網絡. 約翰的網絡是樹形的,連接着N(1≤N≤10000)個牛棚.她打算切斷某一個牛棚的電源,使和這個牛棚相連的所有電纜全部中斷.之後,就會存在若干子網絡.爲保證破壞夠大,每一個子網的牛棚數不得超過總牛棚數的一半,那哪些牛棚值得破壞呢?
Input
第1行:一個整數N.
第2到N+1行:每行輸入兩個整數,表示一條電纜的兩個端點.
Output
按從小到大的順序,輸出所有值得破壞的牛棚.如果沒有一個值得破壞,就輸出“NONE”.
Sample Input
10
1 2
2 3
3 4
4 5
6 7
7 8
8 9
9 10
3 8
Sample Output
3
8
HINT
題解
這道題的技巧在於求一個割點分開的連通塊的大小。
一個割點將搜索樹中其子樹的點每個子樹一個連通塊,和這個點上面的點這幾個連通塊。我們在求出割點的時候判斷一下這個點是否值得割。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#define MAXN 10000+10
#define MAXM 10000+10
using namespace std;
int head[MAXN],num,n,m;
int dfn[MAXN],low[MAXN],vis[MAXN],dfnum,root,son;
int ans[MAXN],cnt[MAXN];
stack<int> st;
struct Edge{
int from,to,next;
}edge[MAXN<<1];
void add(int from,int to)
{
edge[++num].next=head[from];
edge[num].from=from;
edge[num].to=to;
head[from]=num;
}
void tarjan(int x,int fa)
{
int flag=1,tmp=0;
dfn[x]=low[x]=++dfnum;
vis[x]=1;
for(int i=head[x];i;i=edge[i].next)
{
if(edge[i].to==fa) continue;
if(!dfn[edge[i].to])
{
tarjan(edge[i].to,x);
low[x]=min(low[x],low[edge[i].to]);
vis[x]+=vis[edge[i].to];
if(dfn[x]<=low[edge[i].to])
{
if(vis[edge[i].to]>n/2) flag=0;
tmp+=vis[edge[i].to];
}
}else
if(vis[edge[i].to])
low[x]=min(low[x],dfn[edge[i].to]);
}
if(n-1-tmp>n/2) flag=0;
if(flag==1)
{
if(!cnt[x]) cnt[0]++,cnt[x]=1;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
tarjan(1,0);
if(cnt[0]==0) {printf("NONE\n");return 0;}
for(int i=1;i<=n;i++)
{
if(cnt[i]==1) printf("%d\n",i);
}
return 0;
}