From 9a06eb082131cee3493ad968b7319a9b13971df9 Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Thu, 2 Oct 2025 20:20:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0Tarjan=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E6=9F=A5=E6=89=BE=E7=82=B9=E5=8F=8C=E8=BF=9E=E9=80=9A?= =?UTF-8?q?=E5=88=86=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加Tarjan算法实现来查找图中的点双连通分量(v-BCC)。包括处理孤立节点和自环边的情况,并输出每个v-BCC的节点列表。使用手动栈优化性能,并添加详细注释说明算法逻辑。 --- src/10/2/P8435.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/src/10/2/P8435.cpp b/src/10/2/P8435.cpp index 663abb6..980ca7f 100644 --- a/src/10/2/P8435.cpp +++ b/src/10/2/P8435.cpp @@ -1,9 +1,100 @@ -#include +#include +#include +#include -using ll = int64_t; +const int MAXN = 5e5+5; // 节点数上限 +int n, m; +std::vector adj[MAXN]; // 邻接表存图 +int dfn[MAXN], low[MAXN], ts; // Tarjan算法所需的时间戳数组 +int stk[MAXN], top; // 手动实现的栈,比std::stack稍快 +bool isbcc[MAXN]; // 标记节点是否已属于某个v-BCC -int main(){ +std::vector> bcc; // 存储所有找到的点双连通分量 + +void tarjan(int u, int parent) { + dfn[u] = low[u] = ++ts; + stk[++top] = u; + + // 对于孤立点,若没有边,则tarjan函数会直接结束。 + // 我们将在main函数中处理这种情况。 + + for (int v : adj[u]) { + // 在无向图中,(u,v)和(v,u)是同一条边,避免访问刚过来的边 + if (v == parent) { + continue; + } + + if (!dfn[v]) { // v未被访问,是u在DFS树中的子节点 + tarjan(v, u); + low[u] = std::min(low[u], low[v]); + + // 关键判断:v及其子树无法到达u的祖先 + if (low[v] >= dfn[u]) { + std::vector curbcc; + int node; + // 从栈中弹出节点,直到v被弹出 + do { + node = stk[top--]; + curbcc.push_back(node); + isbcc[node] = true; + } while (node != v); + + // 割点u也属于这个v-BCC + curbcc.push_back(u); + isbcc[u] = true; + + bcc.push_back(curbcc); + } + } else { // v已被访问,(u,v)是返祖边 + low[u] = std::min(low[u], dfn[v]); + } + } +} + +int main() { + std::ios_base::sync_with_stdio(false); + std::cin.tie(nullptr); + + std::cin >> n >> m; + + for (int i = 0; i < m; ++i) { + int u, v; + std::cin >> u >> v; + // 题目允许重边和自环。对于v-BCC,重边和一条边效果一样,自环可特殊处理 + // 此处我们正常建图,让tarjan处理 + if (u == v) continue; // v-BCC定义通常不考虑自环,但题目样例有,先忽略自环边,最后处理孤立点 + adj[u].push_back(v); + adj[v].push_back(u); + } + // 遍历所有节点,确保处理完所有连通分量 + for (int i = 1; i <= n; ++i) { + if (!dfn[i]) { + // 如果一个节点是孤立的(或只与已访问过的部分相连), + // tarjan调用后栈可能为空或只含自己。 + // 为了简化,我们只在tarjan内部处理连通的组件。 + tarjan(i, 0); + } + } + + // 最后的清扫:处理所有未被分配到任何v-BCC的节点 + // 这些通常是孤立点,或者图中只有一个节点的情况,包括有自环的孤立点 + for (int i = 1; i <= n; ++i) { + if (!isbcc[i]) { + bcc.push_back({i}); + } + } + + + // 输出结果 + std::cout << bcc.size() << "\n"; + for (const auto& ret : bcc) { + std::cout << ret.size(); + for (int node : ret) { + std::cout << " " << node; + } + std::cout << "\n"; + } } \ No newline at end of file