$\ \ \ \ \ \ \ \,$tarjian算法及其相关算法的复习笔记:
边双连通分量
$\ \ \ \ \ \ \ \,$对于一个有向图,能互相到达的点在一个连通分量,很多时候一个连通分量的点对答案没有影响,或者可以统一安排,那么我们就用tarjan算法把它们缩在一起。
$\ \ \ \ \ \ \ \,$这样就把有向图转换为了一个DAG,会方便处理很多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| int U[N],V[N],p; vector<int> G[N]; void add(int a,int b){U[++p]=a;V[p]=b;G[a].push_back(b);} int sta[N],top; int low[N],dfn[N],tim; int col[N],mark; void tarjan(int x){ sta[++top]=x; low[x]=dfn[x]=++tim; for(auto v:G[x]){ if(!dfn[v])tarjan(v); if(!col[v])low[x]=min(low[x],low[v]); } if(low[x]==dfn[x]){ mark++; while(sta[top+1]!=x)col[sta[top]]=mark,top--; } }
|
割点
$\ \ \ \ \ \ \ \,$在一个无向图中,去掉一个点,使得图不连通,这个点就叫割点(sta[]中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| vector<int> G[N]; void add(int a,int b){G[a].push_back(b);G[b].push_back(a);} int sta[N],top; int low[N],dfn[N],tim; bool used[N]; void tarjan(int x,int rt){ int cnt=0; low[x]=dfn[x]=++tim; for(auto v:G[x]){ if(!dfn[v]){ tarjan(v,rt); low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]&&x!=rt&&used[x]==0) {sta[++top]=x;used[x]=1;} if(x==rt)cnt++; } low[x]=min(low[x],dfn[v]); } if(x==rt&&cnt>=2&&used[x]==0) {sta[++top]=x;used[x]=1;} }
|
圆方树
$\ \ \ \ \ \ \ \,$专门处理仙人掌的做法,把一个环转换为一个方点,把一个仙人掌转换为一棵树,方便处理:
$\ \ \ \ \ \ \ \,$(方点之间连接的圆点为割点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int sta[N],top,size,tim,dfn[N],low[N]; vector<int> G[N],E[N<<1]; void add1(int a,int b){G[a].push_back(b);G[b].push_back(a);} void add2(int a,int b){E[a].push_back(b);E[b].push_back(a);} void tarjan(int x){ dfn[x]=low[x]=++tim; sta[++top]=x; for(auto v:G[x]){ if(!dfn[v]){ tarjan(v),low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]){ ++size; while(sta[top+1]!=v)add2(sta[top],size),top--; add2(x,size); } } low[x]=min(low[x],dfn[v]); } }
|