圆方树
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
int n,m;
int to[maxn<<1],nxt[maxn<<1],h[maxn],tot;//建原图
inline void adde(int x,int y){
to[++tot]=y;
nxt[tot]=h[x];
h[x]=tot;
}
int to1[maxn<<2],nxt1[maxn<<2],h1[maxn<<1],tot1;//建新树,因为割点的数量小于 n,圆方树的点数小于2n,各种数组大小要开两倍
inline void adde1(int x,int y){
to1[++tot1]=y;
nxt1[tot1]=h1[x];
h1[x]=tot1;
}
int dfn[maxn],low[maxn],cnt,sum;//dfn[]为dfs序,low[]是只经过不多于1条返祖边能到达的最小dfs序
int stk[maxn],top;
void tarjan(int u){
low[u]=dfn[u]=++cnt;
stk[++top]=u;//该点进栈
for(int i=h[u];i;i=nxt[i]){
int y=to[i];
if(!dfn[y]){//未访问过的点,非返祖边
tarjan(y);
low[u]=min(low[u],low[y]);
if(low[y]==dfn[u]){//找到一个点双连通分量
++sum;//新方点
for(int j=0;j!=y;top--){//!!!不能用while弹到u点
j=stk[top];
adde1(sum,j);
adde1(j,sum);
}
adde1(u,sum);
adde1(sum,u);
}
}
else low[u]=min(low[u],dfn[y]);//只能经过一次返祖边,只能用dfn[y]更新low[u]
}
}
int main(){
std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
sum=n;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
adde(u,v);
adde(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i),top=0;
return 0;
}
关于出栈
Wrong:
while(stk[top]!=x){
adde1(stk[top],sum);
adde1(sum,stk[top]);
top--;
}
Right:
for(int j=0;j!=y;top--){
j=stk[top];
adde1(sum,j);
adde1(j,sum);
}
Reason: 确定点双应该在返回祖先节点后确定,如果以祖先节点为基准,可能会导致在遍历祖先节点和尾节点顺序之间而不属于该点双连通分量的节点被弹出并加入该点双连通分量。

浙公网安备 33010602011771号