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;
}