UESTC 900 方老师炸弹 --Tarjan求割点及删点后连通分量数

时间:2022-03-21 05:49:11

Tarjan算法。

1.若u为根,且度大于1,则为割点

2.若u不为根,如果low[v]>=dfn[u],则u为割点(出现重边时可能导致等号,要判重边)

3.若low[v]>dfn[u],则边(u,v)为桥(封死在子树内),不操作。

求割点时,枚举所有与当前点u相连的点v:

1.是重边: 忽略

2.是树边: Tarjan(v),更新low[u]=min(low[u],low[v]); 子树个数cnt+1.如果low[v] >= dfn[u],说明是割点,割点数+1

3.是回边: 更新low[u] = min(low[u],dfn[v]),因为此时v是u的祖先。

关于Tarjan求割点可见:http://hi.baidu.com/lydrainbowcat/item/f8a5ac223e092b52c28d591c

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#define Mod 1000000007
using namespace std;
#define N 10007 vector<int> G[N];
struct CUT
{
int v,num;
}cut[N];
int vis[N],dfn[N],low[N],Time;
int n,m,k; int cmp(CUT ka,CUT kb)
{
if(ka.num == kb.num)
return ka.v < kb.v;
return ka.num > kb.num;
} void Tarjan(int u,int fa)
{
low[u] = dfn[u] = ++Time;
int cnt = ;
vis[u] = ;
int flag = ;
for(int i=;i<G[u].size();i++)
{
int v = G[u][i];
if(v == fa && flag) //重边
{
flag = ;
continue;
}
if(!vis[v]) //树边
{
Tarjan(v,u);
cnt++;
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u])
cut[u].num++;
}
else if(vis[v] == ) //回边
low[u] = min(low[u],dfn[v]);
}
if(fa == - && cnt == ) //为根且度数为1,不是割点
cut[u].num = ;
vis[u] = ;
return;
} int main()
{
int i,j,u,v;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
Time = ;
for(i=;i<=n;i++)
{
G[i].clear();
cut[i].v = i;
cut[i].num = ;
}
cut[].num = ; //根要特判
while(m--)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low));
memset(vis,,sizeof(vis));
Tarjan(,-);
sort(cut,cut+n,cmp);
for(i=;i<k;i++)
printf("%d %d\n",cut[i].v,cut[i].num);
printf("\n");
}
return ;
}