P5058 [ZJOI2004] 嗅探器

https://www.luogu.com.cn/problem/P5058

思路

明显这个点需要是割点
我们在用Tarjan求割点时,要判断割点是否把 a 和 b 必须分割到两个不连通的部分中。
为了简化问题,我们直接把a点作为根,从a点开始做DFS。

开头想是不是 判断b的时间戳(dfn)是否不小于我们找到的割点u就可以?
很快就否定了,如下情况就不行:
image

所以我们就从v点入手。 如果dfn[b]>=dfn[v] 那就肯定割的掉。 因为这种情况下,b在v的搜索子树内。

洛谷上其他人分析如下:

image

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200000 + 5;

int n;                          // 服务器数
vector<int> g[MAXN];            // 邻接表
int a, b;                       // 两个信息中心编号
int dfn[MAXN], low[MAXN], tim;  // 时间戳数组
bool vis[MAXN];                 // 访问标记
vector<int> cuts;               // 满足条件的割点

// 在 u 上做 dfs,par 是父节点
void dfs(int u, int par) {
    vis[u] = true;
    dfn[u] = low[u] = ++tim;

    for (int v : g[u]) {
        if (!vis[v]) {
            dfs(v, u);
            low[u] = min(low[u], low[v]);

            // 如果u是割点, 能割掉 v
            if (low[v] >= dfn[u] && u != a && u != b) {
                // b 在 v 的子树中
                if(dfn[b]>=dfn[v])
                    cuts.push_back(u);
            }
        }
        else if (v != par) {
            // 回边
            low[u] = min(low[u], dfn[v]);
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // 读入
    cin >> n;
    int u, v;
    while (cin >> u >> v) {
        if (u == 0 && v == 0) break;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cin >> a >> b;

    // 初始化
    tim = 0;
    // 从 a 出发做一次 DFS
    dfs(a, 0);

 
    if (cuts.empty()) {
        cout << "No solution\n";
    } else {
        // 选最小编号
        cout << *min_element(cuts.begin(), cuts.end()) << "\n";
    }
    return 0;
}

posted @ 2025-05-15 17:57  katago  阅读(15)  评论(0)    收藏  举报