P3388 【模板】割點(割頂)
阿新 • • 發佈:2018-12-15
- 對於非根節點,判斷是不是割點就有些麻煩了。我們維護兩個陣列dfn[]和low[],dfn[u]表示頂點u第幾個被(首次)訪問,low[u]表示頂點u及其子樹中的點,通過非父子邊(回邊),能夠回溯到的最早的點(dfn最小)的dfn值(但不能通過連線u與其父節點的邊)。對於邊(u, v),如果low[v]>=dfn[u],此時u就是割點。
- 但這裡也出現一個問題:怎麼計算low[u]。
- 假設當前頂點為u,則預設low[u]=dfn[u],即最早只能回溯到自身。
- 有一條邊(u, v),如果v未訪問過,繼續DFS,DFS完之後,low[u]=min(low[u], low[v]);
- 如果v訪問過(且u不是v的父親),就不需要繼續DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。
-
#include<bits/stdc++.h> using namespace std; #define maxn 200010 struct node { int v,to; } edge[maxn]; int n,m,ind,cnt,tot; int head[maxn],dfn[maxn]; int low[maxn],a,b; bool cut[maxn]; void add(int x,int y) { edge[++cnt].v=y; edge[cnt].to=head[x]; head[x]=cnt; } void tarjan(int u,int root) { dfn[u]=low[u]=++ind; int children=0; for(int i=head[u]; i!=-1; i=edge[i].to) { int v=edge[i].v; if(!dfn[v]) { tarjan(v,root); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=root) cut[u]=1; if(u==root) children++; } low[u]=min(low[u],dfn[v]); } if(children>=2&&u==root) cut[u]=1; } int main() { memset(dfn,0,sizeof(dfn)); memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } for(int i=1; i<=n; i++) if(dfn[i]==0) tarjan(i,i); for(int i=1; i<=n; i++) if(cut[i]) tot++; printf("%d\n",tot); for(int i=1; i<=n; i++) if(cut[i]) printf("%d ",i); return 0; }