前情提要
关于图的数据结构
template<int MaxN,int MaxM>
struct Graphs{
int Last[MaxN],cntn;
int To[MaxM],Next[MaxM],cntm;
Graphs(){
cntn=cntm=0;
memset(Last,-1,sizeof(Last));
}
inline void Clear(){
cntn=cntm=0;
memset(Last,-1,sizeof(Last));
}
};
template<typename Typename>
inline void Insert(Typename&G,int from,int to){
G.To[G.cntm]=to;
G.Next[G.cntm]=G.Last[from];G.Last[from]=G.cntm++;
}
若有特殊情况 , , , 会特殊标明 . . .
注意初始化
无向图连通性
定义
D
f
n
x
Dfn_x
Dfnx 为点
x
x
x 的时间戳
.
.
.
L
o
w
x
Low_x
Lowx 为满足以下条件点的
D
f
n
Dfn
Dfn 的最小值
.
.
.
- 搜索树中以 x x x 为根的子树中的节点 . . .
- 通过 1 1 1 条不在搜索树上的边能够到达上述节点的节点 . . .
无向图的割边
若无向边
(
x
,
y
)
(x,y)
(x,y) 是割边
,
,
, 满足
:
:
:
(
(
( 其中
y
′
y'
y′ 为搜索树中
x
x
x 的子节点
)
)
)
D
f
n
x
<
L
o
w
y
′
Dfn_x<Low_{y'}
Dfnx<Lowy′
根据定义
,
,
, 说明如果不通过边
(
x
,
y
)
(x,y)
(x,y)
,
,
,
y
′
y'
y′ 不能回到
x
x
x 之前的节点
,
,
, 故边
(
x
,
y
)
(x,y)
(x,y) 为原图的一条割边
.
.
.
Graphs<MaxN,MaxM>G;
int Dfn[MaxN],Low[MaxN],tot;
bool Bridge[MaxM];
inline void Tarjan(int from,int last){
Dfn[from]=Low[from]=++tot;
for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
if(!Dfn[G.To[k]]){
Tarjan(G.To[k],k);
Low[from]=Min(Low[from],Low[G.To[k]]);
if(Low[G.To[k]]>Low[from])
Bridge[k]=Bridge[k^1]=true;
}
else if(k!=last^1)
Low[from]=Min(Low[from],Dfn[G.To[k]]);
}
}
inline void Work(){
Insert(G,x,y);
Insert(G,y,x);
}
第 13 13 13 行的判断语句是为了防止父节点的 L o w Low Low 更新现有节点 , , , 其中 l a s t ⊕ 1 last\oplus1 last⊕1 是因为无向图的两条边编号是相邻的 , , , 编号异或 1 1 1 就是反边的编号 , , , 后面的网络流也会有这种技巧 . . .
无向图的割点
若点
x
x
x 是割点
,
,
, 满足
:
:
:
(
(
( 其中
y
′
y'
y′ 为搜索树中
x
x
x 的子节点
)
)
)
D
f
n
x
≤
L
o
w
y
′
,
x
≠
r
o
o
t
Dfn_x\leq Low_{y'},x\not=root
Dfnx≤Lowy′,x=root
特别的
,
,
, 若
x
x
x 为搜索树的根节点
,
,
,
x
x
x 为割点当且仅当搜索树上存在至少两个子节点
y
1
′
,
y
2
′
y_1',y_2'
y1′,y2′ 满足上述条件
.
.
.
根据定义 , , , D f n x = L o w y ′ Dfn_x=Low_{y'} Dfnx=Lowy′ 时 , , , 原图就被割点分成两个部分 , , , y ′ y' y′ 所在的一边与 x x x 有多条边相连 . . .
Graphs<MaxN,MaxM>G;
int Dfn[MaxN],Low[MaxN],tot;
bool Cut[MaxM];
inline void Tarjan(int from,int root){
Dfn[from]=Low[from]=++tot;
int flag=0;
for(register int k=G.Last[k];k!=-1;k=G.Next[k]){
if(!Dfn[G.To[k]]){
Tarjan(G.To[k],root);
Low[from]=Min(Low[from],Low[G.To[k]]);
if(Dfn[from]<=Low[G.To[k]]){
flag++;
if(from!=root||flag>1)
Cut[from]=true;
}
}
else Low[from]=Min(Low[from],Dfn[G.To[k]]);
}
}
inline void Work(){
for(register int i=1;i<=n;i++)
if(!Dfn[i])Tarjan(i,i);
}
注意事项 : : :
- 第
17
17
17 行的原则应判断
G.To[k]!=dad, , , 但出于简洁和不影响正确性的前提 , , , 舍去了判断 , , , 请读者理解 . . .
点双连通分量缩点
每一个割点可能在多个点双连通分量 . . .
Graphs<MaxN,MaxM>G;
Stack<int,MaxN>S;
vector<int>VDcc[MaxN];
int Dfn[MaxN],Low[MaxN],tot;
bool Cut[MaxM];
int Belong[MaxN],vdcc;
inline void Tarjan(int from,int root){
Dfn[from]=Low[from]=++tot;
S.Push(from);
if(from==root&&G.Last[from]==-1){
VDcc[++vdcc].push_back(from);
return;
}
int flag=0;
for(register int k=G.Last[k];k!=-1;k=G.Next[k]){
if(!Dfn[G.To[k]]){
Tarjan(G.To[k],root);
Low[from]=Min(Low[from],Low[G.To[k]]);
if(Dfn[from]<=Low[G.To[k]]){
flag++;
if(from!=root||flag>1)
Cut[from]=true;
vdcc++;int now;
do{
now=S.Top();S.Pop();
VDcc[vdcc].push_back(now);
}while(now!=G.To[k]);
VDcc[vdcc].push_back(from);
}
}
else Low[from]=Min(Low[from],Dfn[G.To[k]]);
}
}
Graphs<2*MaxN,2*MaxN>Tree;
inline void Work(){
num=vdcc;
for(register int i=1;i<=n;i++)
if(Cut[i])Id[i]=++num;
for(register int i=1;i<=vdcc;i++){
int vsize=VDcc[i].size();//安全求稳
for(register int j=0;j<vsize;j++){
if(Cut[VDcc[i][j]]){
Insert(Tree,i,Id[VDcc[i][j]]);
Insert(Tree,Id[VDcc[i][j]],i);
}
else Belong[VDcc[i][j]]=i;
}
}
}
边双连通分量缩点
int Belong[MaxN],edcc;
inline void Dfs(int from){
Belong[from]=edcc;
for(rgeister int k=G.Last[from];k!=-1;k=G.Next[k]){
if(Belong[G.To[k]]||Bridge[k])continue;
Dfs(G.To[k]);
}
}
Graphs<MaxN,MaxN>Tree;
inline void Work(){
for(register int i=1;i<=n;i++){
if(!Belong[i]){
edcc++;
Dfs(i);
}
}
for(register int i=0;i<G.cntm;i++){
if(Belong[G.To[i^1]]==Belong[G.To[i]])continue;
Insert(Tree,Belong[G.To[i^1]],Belong[G.To[i]]);
}
}
注意事项 : : :
- 第 21 21 21 行没有连双向边的原因是这条边的反边也会被遍历 , , , 要理解透彻 . . .
有向图连通性
定义
T a r j a n Tarjan Tarjan 算法在 D f s Dfs Dfs 中维护了一个栈 : : :
- 搜索树上 x x x 的祖先节点 . . .
- 已访问的 , , , 存在一条路径到达 1 1 1 类的节点 . . .
D
f
n
x
Dfn_x
Dfnx 为点
x
x
x 的时间戳
.
.
.
L
o
w
x
Low_x
Lowx 为满足以下条件点的
D
f
n
Dfn
Dfn 的最小值
.
.
.
- 在栈中的节点 . . .
- 通过 1 1 1 条从搜索树上出发的有向边能到达的节点 . . .
强连通分量缩点
如果节点
x
x
x 的栈构成一个强连通分量
,
,
, 满足
:
:
:
D
f
n
x
=
L
o
w
x
Dfn_x=Low_x
Dfnx=Lowx
然而我不会证明
.
.
.
其实就是我的搜索子树到不了祖先 , , , 至于那些出栈的点 , , , 他自己都没能出来 , , , 不可能能到我们这 . . .
Graphs<MaxN,MaxM>G;
Stack<int,MaxN>S;
vector<int>Scc[MaxN];
int Dfn[MaxN],Low[MaxM],tot,scc;
bool Visit[MaxN];
inline void Tarjan(int from){
Dfn[from]=Low[from]=++tot;
S.Push(from);Visit[from]=true;
for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
if(!Dfn[G.To[k]]){
Tarjan(G.To[k]);
Low[from]=Min(Low[from],Low[G.To[k]]);
}
else if(Visit[G.To[k]])
Low[from]=Min(Low[from],Dfn[G.To[k]]);
}
if(Dfn[from]==Low[from]){
scc++;int now;
do{
now=S.Top();S.Pop();Visit[now]=false;
Belong[now]=scc;Scc[scc].push_back(now);
}while(from!=now);
}
}
Graphs<MaxN,MaxN>Tree;
inline void Work(){
for(register int i=1;i<=n;i++)
for(register int k=G.Last[i];k!=-1;k=G.Next[k]){
if(Belong[i]==Belong[G.To[k]])continue;
Insert(Tree,Belong[i],Belong[G.To[k]]);
}
}
1317



被折叠的 条评论
为什么被折叠?



