洛谷 P3388 【模板】割点(割顶)(Tarjan)
题目链接
https://www.luogu.org/problemnew/show/P3388
模板题
解题思路
怎样求割点?
- dfn :即时间戳,一张图的dfs序(dfs遍历时出现的顺序)
- 树边:连向孩子的边
- 反向边:连向祖先的边
- low :即一个点能到达的时间戳最小的边(反向边只能经过一条)
显然,一个点如果它的任意一个孩子的low大于等于这个点,那么这个点就是割点。
AC代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<set> 5 #include<stack> 6 using namespace std; 7 const int maxn=20005; 8 const int maxm=200005; 9 int n,m,cnt,dfn[maxn],low[maxn],times,p[maxn]; 10 bool iscut[maxn]; 11 struct edge{ 12 int u; 13 int v; 14 int next; 15 }e[maxm]; 16 stack<edge> S; 17 void insert(int u,int v){ 18 ++cnt; 19 e[cnt].u=u; 20 e[cnt].v=v; 21 e[cnt].next=p[u]; 22 p[u]=cnt; 23 } 24 void dfs(int u,int fa){ 25 dfn[u]=low[u]=++times; 26 int child=0; 27 for(int i=p[u];i!=-1;i=e[i].next){ 28 int v=e[i].v; 29 if(dfn[v]==0){ 30 child++; 31 dfs(v,u); 32 low[u]=min(low[u],low[v]); 33 if(low[v]>=dfn[u])iscut[u]=true; 34 } 35 else if(dfn[v]<dfn[u]&&v!=fa){ 36 low[u]=min(low[u],dfn[v]); 37 } 38 } 39 if(fa==-1&&child==1) iscut[u]=false; 40 } 41 int main() 42 { 43 cin>>n>>m; 44 memset(p,-1,sizeof(p)); 45 memset(low,0x3f3f3f,sizeof(low)); 46 for(int i=1;i<=m;i++){ 47 int x,y; 48 scanf("%d%d",&x,&y); 49 insert(x,y); 50 insert(y,x); 51 } 52 for(int i=1;i<=n;i++){ 53 if(!dfn[i]) dfs(i,-1); 54 } 55 int ans=0; 56 for(int i=1;i<=n;i++){ 57 if(iscut[i]) ans++; 58 } 59 cout<<ans<<endl; 60 for(int i=1;i<=n;i++){ 61 if(iscut[i]) cout<<i<<" "; 62 } 63 return 0; 64 }