连通性问题 tarjan

强连通分量

P3387 【模板】缩点 / 强连通分量 为例:

什么是强连通分量:在有向图中,某些点可以相互到达(在同一个强连通分量中的点 \(i,j\) ,从 \(i\) 可以到达 \(j\) ,从 \(j\) 也可以到达 \(i\) )。

对每个强连通分量缩为一个点后,会形成一个 DAG (有向无环图)。接下来可能就是拓扑排序。

在有向图中搜索,有几个概念:

树边:第一次到达某个点时所经过的边

返祖边:从当前节点联通祖先的边

横插边:从当前点连向已访问的兄弟子树的边

dfn:按照树边搜索的 dfs序

low:子树内沿着一条返祖边能到达的最小 dfn。

将图想象成以树边为主的图,沿着返祖边才能构成连接上下的环,能构成强连通分量。沿着横插边仅能访问其他强连通分量,如果没有返祖边沟通,两点之间不能互相到达。如果已经访问的节点属于某个强连通分量,则是已经跑完 tarjan的兄弟子树,该边判断为横插边。

在栈内存储访问的节点。在进入 \(x\) 节点后,跑完子树内的 tarjan,如果 \(dfn_x=low_x\) ,则从栈顶到 \(x\) 为止属于一个强连通分量。(1.子树内其他的强连通分量已经通过同样的方式弹出栈,因此剩余节点的 \(low_i=dfn_x\)。2.\(low_x=dfn_x\) ,说明剩余节点都不能访问到 \(x\) 的祖先,所以与 \(x\) 强连通)

Code:

vector<int>v[N];
int s[N],top;
int cnt,dfn[N],low[N];
int tot,bel[N];
void tarjan(int x){
    dfn[x]=low[x]=++cnt;
    s[++top]=x;
    for(auto i:v[x]){
        if(!dfn[i]){
            tarjan(i);
            low[x]=min(low[x],low[i]);
        }
        else if(!bel[i]){
            low[x]=min(low[x],dfn[i]);
        }
    }
    if(dfn[x]==low[x]){
        tot++;
        while(top!=0){
            int now=s[top--];
            bel[now]=tot;
            if(now==x)break;
        }
    }
}

其实如果按照强连通分量的序号从大到小,就是拓扑序。(先遍历各个儿子,最后回到节点给新编号)

有趣例题(不包括一些做过或模板题):

例#1 P2812 校园网络【[USACO]Network of Schools加强版】

问题:共有 \(n\) 所学校 \((1 \leq n \leq 10^4)\) 已知他们实现设计好的网络共 \(m\) 条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。

对于所有数据,有 \(1 \leq n \leq 10^4\),$1\le $ 线路数 \(\le 5 \times 10^4\)

讨论入度,构造。

分析:第一问,先缩点,入度为 \(0\) 的是选择的母机。第二问,入度为 \(0\) ,出度为 \(0\) 的点依次相连,让整个图构成一个强连通分量,答案是 \(\max(tot1,tot2)\)

例#2 P10944 Going from u to v or from v to u?

问题:如果 \(u\) 能到达 \(v\) ,或 \(v\) 能到达 \(u\) ,叫两点间有路。问是否所有两点间有路?

\(1 \le n \le 1000,\ 1 \le m \le 6000\)

缩点后是一条链(不算重边)。判断链

分析:所有都有路。如果是强连通分量内部,一定有路。缩点后构成 DAG ,如果进/出某个点有岔路,岔开的点不能相互到达。所以是链。拓扑,如果queue中元素个数 \(\ge 2\) ,说明不是链。

例#3 P1407 [国家集训队] 稳定婚姻

问题:我们已知 \(n\) 对夫妻的婚姻状况,称第 \(i\) 对夫妻的男方为 \(B_i\),女方为 \(G_i\)。若某男 \(B_i\) 与某女 \(G_j\) 曾经交往过(无论是大学,高中,亦或是幼儿园阶段,\(i \neq j\)),则当某方与其配偶(即 \(B_i\)\(G_i\)\(B_j\)\(G_j\))感情出现问题时,他们有私奔的可能性。不妨设 \(B_i\) 和其配偶 \(G_i\) 感情不和,于是 \(B_i\)\(G_j\) 旧情复燃,进而 \(B_j\) 因被戴绿帽而感到不爽,联系上了他的初恋情人 \(G_k\) ……一串串的离婚事件像多米诺骨牌一般接踵而至。若在 \(B_i\)\(G_i\) 离婚的前提下,这 \(2n\) 个人最终依然能够结合成 \(n\) 对情侣,那么我们称婚姻 \(i\) 为不安全的,否则婚姻 \(i\) 就是安全的。

给定所需信息,你的任务是判断每对婚姻是否安全。\(1 \le n \le 4000\)\(0 \le m \le 20000\)

图构建

分析:设想是,如果 \(B_i\)\(G_i\) 分开,\(G_i\) 会找到 \(B_j\) ,而对应的 \(G_j\) 会找 \(B_k\) \(...\) 不断的找下去,但最终 \(B_i\) 不能缺,最终会回到 \(B_i\) 。按照以上过程连接边。情人关系( \(m\) 条关系),是 \(G\) 找到 \(B\) ,所以从左向右连;夫妻关系( \(n\) 条关系),是 \(B_j\) 配对后,找到 \(G_j\) 让它重新配对,所以是从右向左连。连接图后,如果是不安全的,从 \(G_i\) 走应能到达 \(B_i\)\(B_i\) 又直接与 \(G_i\) 连接,所以 \(B_i\ ,G_i\) 在同一个强连通分量内。

例#4 P2272 [ZJOI2007] 最大半连通子图

问题:一个有向图 \(G=\left(V,E\right)\) 称为半连通的 (Semi-Connected),如果满足:\(\forall u,v\in V\),满足 \(u\to v\)\(v\to u\),即对于图中任意两点 \(u,v\),存在一条 \(u\)\(v\) 的有向路径或者从 \(v\)\(u\) 的有向路径。

\(G'=\left(V',E'\right)\) 满足 \(V'\subseteq V\)\(E'\)\(E\) 中端点属于 \(V'\) 的所有边,则称 \(G'\)\(G\) 的一个导出子图。若 \(G'\)\(G\) 的导出子图,且 \(G'\) 半连通,则称 \(G'\)\(G\) 的半连通子图。若 \(G'\)\(G\) 所有半连通子图中包含节点数最多的,则称 \(G'\)\(G\) 的最大半连通子图。

给定一个有向图 \(G\),请求出 \(G\) 的最大半连通子图拥有的节点数 \(K\),以及不同的最大半连通子图的数目 \(C\)。由于 \(C\) 可能比较大,仅要求输出 \(C\)\(X\) 的余数。

对于 \(100\%\) 的数据,\(N\le 10^5\)\(M\le 10^6\)\(X\le 10^8\)

典型缩点后拓扑dp

分析缩点后是一条链(不算重边)。目标是求出有最多节点数的链,同时计数。要去除重边,否则在计数时会出现错误。

posted @ 2026-08-11 22:41  TanHaoren  阅读(5)  评论(0)    收藏  举报