无向图的边双连通分量
求桥
变量
- int dfn[i]:点 i 的时间戳。
- int low[i]:点 i 的追溯值。
- int num:统计时间戳的中间变量。
- bool bri[i]:边 i 是否为桥。
函数
- void tarjan(int x,int in):求从点 x 开始,入边为 in 时的桥。
代码
int dfn[N],low[N],num;
bool bri[M<<1];
void tarjan(int x,int in){
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])bri[i]=bri[i^1]=1;
}
else if(i!=(in^1))low[x]=min(low[x],dfn[y]);
}
}
缩点
变量
- int dfn[i]:点 i 的时间戳。
- int low[i]:点 i 的追溯值。
- int num:统计时间戳的中间变量。
- bool bri[i]:边 i 是否为桥。
- int col[i]:点 i 所属的边双连通分量。
- int dcc:边双个数。
函数
- void tarjan(int x,int in):求从点 x 开始,入边为 in 时的桥。
- void dfs(int x):求出删去桥后形成的连通块。
代码
int dfn[N],low[N],num;
bool bri[M<<1];
int col[N],dcc;
void tarjan(int x,int in){
dfn[x]=low[x]=++num;
for(int i=G1.head[x];i;i=G1.nxt[i]){
int y=G1.ver[i];
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]){
bri[i]=bri[i^1]=1;
}
}
else if(i!=(in^1))low[x]=min(low[x],dfn[y]);
}
}
void dfs(int x){
col[x]=dcc;
s[dcc]+=a[x];
for(int i=G1.head[x];i;i=G1.nxt[i]){
int y=G1.ver[i];
if(col[y]||bri[i])continue;
dfs(y);
}
}
无向图的点双连通分量
求割点
变量
- int dfn[i]:点 i 的时间戳。
- int low[i]:点 i 的追溯值。
- int num:统计时间戳的中间变量。
- int root:图的根。
- bool cut[i]:点 i 是否为割点。
函数
- void tarjan(int x):求从点 x 开始的割点。
代码
int num,root,dfn[N],low[N];
bool cut[N];
void tarjan(int x){
dfn[x]=low[x]=++num;
int flag=0;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y]){
flag++;
if(x!=root||flag>1)
cut[x]=1;
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
圆方树
变量
- int dfn[i]:点 i 的时间戳。
- int low[i]:点 i 的追溯值。
- int num:统计时间戳的中间变量。
- bool cut[i]:点 i 是否为割点。
- int rt:图的根。
- int sta[i]:栈内第 i 个元素。
- int top:栈顶。
- int dcc:圆方树总点数,初始值为 n。
函数
- void tarjan(int x):从点 x 开始,划分圆方树。
代码
int dfn[N],low[N],num,cut[N],rt,sta[N],top,dcc;
void tarjan(int x){
dfn[x]=low[x]=++num;
sta[++top]=x;
int flag=0;
for(int i=G1.head[x];i;i=G1.nxt[i]){
int y=G1.ver[i],z;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y]){
dcc++;flag++;
do{
z=sta[top--];
G2.add(dcc,z);G2.add(z,dcc);
}while(z!=y);
G2.add(dcc,x);G2.add(x,dcc);
if(x!=rt||flag>1)cut[x]=1;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
有向图的强连通分量
缩点
变量
- int dfn[i]:点 i 的时间戳。
- int low[i]:点 i 的追溯值。
- int num:统计时间戳的中间变量。
- int scc:缩点后总点数,初始值为 n。
- bool col[i]:点 i 所在的 scc 的编号。
- int sta[i]:栈内第 i 个元素。
- int top:栈顶。
- int ins[i]:点 i 是否在栈内。
函数
- void tarjan(int x):从点 x 开始,划分强联通分量。
- void init():缩点建 DAG。
代码
int dfn[N],low[N],num,scc,col[N];
int sta[N],top,ins[N];
void tarjan(int x){
dfn[x]=low[x]=++num;
ins[sta[++top]=x]=1;
for(int i=G1.head[x];i;i=G1.nxt[i]){
int y=G1.ver[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
int y;scc++;
do{
ins[y=sta[top--]]=0;
col[y]=scc;
}while(x!=y);
}
}
void init(){
for(int x=1;x<=n;x++)
for(int i=G1.head[x];i;i=G1.nxt[i]){
int y=G1.ver[i];
if(col[x]!=col[y])
G2.add(col[x],col[y]);
}
}