模板——tarjan求割点
在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。
注意求割点中的low定义:
割点中low[u]
记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小)
当(u,v)为树边且low[v] >= dfn[u]
时,节点u才为割点。该式子的含义:以节点v为根的子树所能追溯到最早的祖先节点要么为v要么为u。
根节点需要特判,若图不保证联通,要搜索多次
如果搜到已访问的点且不是父节点,就把low赋值为搜到点的dfn,下面程序可以证明这是正确的
但如果把low赋值为搜到点的low,就有可能影响其父亲的后面low更新(该点搜到它父亲的父亲,导致其父亲的low更新为父亲的父亲的low,这样就违背了low[u]不通过u父亲的原则)
详见样例:
5 6
1 2
1 3
2 3
3 4
4 5
3 5
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=200005; 5 6 vector<int> v[20005]; 7 int n,m; 8 int dfn[N],low[N],cnt=0,vis[N],bl[N],tot=0; 9 10 void dfs(int u,int f) 11 { 12 int ch=0; 13 dfn[u]=++cnt; 14 low[u]=cnt; 15 vis[u]=1; 16 for(int i=0;i<(int)v[u].size();i++) 17 { 18 int p=v[u][i]; 19 if(vis[p]) 20 { 21 if(p!=f) low[u]=min(low[u],dfn[p]); 22 //因为搜到u的点f必定肯定dfn最为接近于dfn[u],故dfn[p]<dfn[f],所以只需赋值成dfn[p] 23 //搜索顺序一定是p->f->u,若f>p,则应是p搜到u(按照dfs顺序) 24 //强连通就需要 low[u]=min(low[u],low[p]); 25 } 26 else 27 { 28 ch++; 29 dfs(p,u); 30 low[u]=min(low[u],low[p]); 31 if(f==-1&&ch>=2) bl[u]=1; 32 if(f!=-1&&low[p]>=dfn[u]) bl[u]=1; 33 } 34 } 35 } 36 37 int main() 38 { 39 scanf("%d%d",&n,&m); 40 for(int i=1;i<=m;i++) 41 { 42 int x,y; 43 scanf("%d%d",&x,&y); 44 v[x].push_back(y); 45 v[y].push_back(x); 46 } 47 for(int i=1;i<=n;i++) 48 { 49 if(!vis[i]) dfs(i,-1);//不一定是连通图 50 } 51 for(int i=1;i<=n;i++) tot+=bl[i]; 52 cout<<tot<<endl; 53 for(int i=1;i<=n;i++) if(bl[i]) printf("%d ",i); 54 }