核心是时间戳+dfs
无向图的dfs森林只有树边和反向边
割点:
①根结点为割点当其有至少2个儿子
②非根结点u为割点当存在子结点v的low[v]>=dfn[u]
桥:
在dfs树中,v是u的儿子,边(u,v)为桥当low[v]>dfn[u]
struct E
{
int to,next,cut;
}edge[maxm];
int head[maxn],tol;
inline void Addedge(int u,int v)
{
edge[tol].to=v;edge[tol].cut=0;
edge[tol].next=head[u];head[u]=tol++;
return;
}
int dfn[maxn],low[maxn],cut[maxn],tot;
inline void Tarjan(int u,int p)
{
dfn[u]=low[u]=++tot;
int pcnt=0,son=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==p&&!pcnt){++pcnt;continue;}
if(!dfn[v]){
++son;
Tarjan(v,u);
if(low[v]<low[u]) low[u]=low[v];
if(low[v]>=dfn[u]) cut[u]=1;
if(low[v]>dfn[u]){
edge[i].cut=edge[i^1].cut=1;
}
}
else if(dfn[v]<low[u]){
low[u]=dfn[v];
}
}
if(!p&&son==1) cut[u]=0;
return;
}
边双连通分量
每个点只属于一个边双连通分量,对点存栈
struct E
{
int to,next;
}edge[maxm];
int head[maxn],tol;
inline void Addedge(int u,int v)
{
edge[tol].to=v;edge[tol].next=head[u];head[u]=tol++;
return;
}
int dfn[maxn],low[maxn],bel[maxn],stk[maxn],top,blocks,tot;
inline void Tarjan(int u,int p)
{
dfn[u]=low[u]=++tot;
stk[top++]=u;
int pcnt=0,v;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].to;
if(v==p&&!pcnt){++pcnt;continue;}
if(!dfn[v]){
Tarjan(v,u);
if(low[v]<low[u]) low[u]=low[v];
}
else if(dfn[v]<low[u]){
low[u]=dfn[v];
}
}
if(low[u]==dfn[u]){
++blocks;
do{
v=stk[--top];
bel[v]=blocks;
}while(v!=u);
}
return;
}
点双连通分量
每条边只属于一个点双连通分量,对边存栈
struct E
{
int to,next;
}edge[maxm];
int head[maxn],tol;
inline void Addedge(int u,int v,int id)
{
edge[tol].to=v;edge[tol].next=head[u];head[u]=tol++;
return;
}
int dfn[maxn],low[maxn],bel[maxn],stk[maxm],top,blocks,tot;
vector<int>bcc[maxn];
inline void Tarjan(int u,int p)
{
dfn[u]=low[u]=++tot;
int pcnt=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==p&&!pcnt){++pcnt;continue;}
if(!dfn[v]){
stk[top++]=i;
Tarjan(v,u);
if(low[v]<low[u]) low[u]=low[v];
if(low[v]>=dfn[u]){
bcc[++blocks].clear();
do{
E&e=edge[stk[--top]];
if(bel[e.from]!=blocks){
bel[e.from]=blocks;
bcc[blocks].push_back(e.from);
}
if(bel[e.to]!=blocks){
bel[e.to]=blocks;
bcc[blocks].push_back(e.to);
}
if(e.from==u&&e.to==v) break;
}while(1);
}
}
else if(dfn[v]<dfn[u]){
//这里要dfn[v]<dfn[u],确保这个点双连通分量求出前加入该边
stk[top++]=i;
low[u]=min(low[u],dfn[v]);
}
}
return;
}
时间复杂度都是O(n+m)