研究了一晚上,终于把割点和强连通搞懂了……(感谢各位学长哈,问了你们那么多简单的东西而你们总是耐心地给我解答~)
大体和Tarjan一样,就是low更新的时候和Tarjan绝对不一样!!!(搞懂这个费了我一个半小时……QAQ)
在求强连通分量时,对于从u->v的边,如果v已经在栈中,那么说明u,v一定在同一个强连通分量中,所以到最后low[u]=low[v]是必然的,提前更新也不会有问题,但是在求割点时,low的定义有了小小的变化,不再是最早能追溯到的祖先,(因为是个无向图)没有意义,应该是最早能绕到的割点,为什么用绕到,是因为是无向边,所以有另一条路可以走,如果把dfn[v]改掉就会上翻过头,可能翻进另一个环中,所以wa掉。
如果你看不懂,建议你画一个从上到下一次连接的三个点,每一个点都有一条到上一个点(它父亲的)反向边,除了最上面的那个,自己试试low和dfn两种更新,你就明白为什么在访问过这个点的情况下,需要用dfn来更新。
另外,根节点要特别判断,所以当现在处理的这条边起点是根节点的时候,我们只是增加其出度,暂时不将它看作是一个割点。出来的时候特别判断一下就可以了。
另外,数组一定要开够,一定要开够,一定要开够!就是因为这个我又没一次A!重要的事说三遍!
下面上代码~
#include<cstdio> #include<iostream> using namespace std; #define maxn 1000005 int head[maxn],to[maxn],nxt[maxn],dfn[maxn],low[maxn]; int n,m,cnt,ans,num; bool cut[maxn]; void add(int a,int b) { to[++cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; } void Tarjan(int u,int fa) { int am=0; dfn[u]=low[u]=++num; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) { Tarjan(v,fa); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=fa) cut[u]=1; if(u==fa) am++; } else low[u]=min(low[u],dfn[v]); } if(u==fa&&am>=2) cut[fa]=1; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i,i); for(int i=1;i<=n;i++) if(cut[i]) ans++; printf("%d ",ans); for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i); return 0; }
另:如果是求割边的话dfn和low取等就不是一条割边了。