//如果u是根节点,只要它有两个子节点就说明是割点,
//否则,满足(u,v)为树枝边(或称父子边,即u是v的父亲),使得dfn[u]<=low[v];
//桥无向边(u,v),当且仅当(u,v)为树枝边,且满足dfn[u]<low[v];
#include<cstring>
#include<string>
#include<cstdio>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define inf 0x3f3f3f3f
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int M=1e6+5;
struct node
{
int v,ne;
}edge[M];
int head[N],dfn[N],low[N];
int iscut[N];//割点标记
int n,top,e;
int cut;
stack<int>sta;
struct Brige
{
int u,v;
}brige[M];
void init()
{
memset(head,-1,sizeof(head));
e=0;cut=0;
}
void add(int a,int b)
{
edge[e].v=b;
edge[e].ne=head[a];
head[a]=e++;
}
void tarjan(int now,int pre)
{
low[now]=dfn[now]=++top;
int son=0;
for(int i=head[now];i!=-1;i=edge[i].ne)
{
int v=edge[i].v;
if(v==pre) continue;
if(!dfn[v])
{
son++;//孩子节点的数目
tarjan(v,now);
low[now]=min(low[now],low[v]);//用后代更新low
if(low[v]>=dfn[now])//判断是否为割点
iscut[now]=1;
if(dfn[now]<low[v])//判断是否为割桥
{
brige[cut].u=min(v,now);
brige[cut++].v=max(now,v);
}
}
else//如果v已被访问,说明(u,v)室反向边,用反向边更新low
low[now]=min(low[now],dfn[v]);
}
if(pre==-1&&son==1)//只有一个孩子节点(割点判断,求割桥试不需要这里)
iscut[now]=0;
}
void solve()
{
top=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(iscut,0,sizeof(iscut));
for(int i=0;i<n;i++)
{
if(!dfn[i])
tarjan(i,-1);
}
}
int main()
{
while(~scanf("%d",&n))
{
int sum,a,b;
init();
for(int i=0;i<n;i++)
{
scanf("%d (%d)",&a,&sum);
for(int j=0;j<sum;j++)
{
scanf("%d",&b);
add(a,b);
}
}
solve();
}
return 0;
}
强连通割点割桥模板
猜你喜欢
转载自blog.csdn.net/qq_41984014/article/details/83994710
今日推荐
周排行