P5058 [ZJOI2004] 嗅探器
https://www.luogu.com.cn/problem/P5058
思路
明显这个点需要是割点
我们在用Tarjan求割点时,要判断割点是否把 a 和 b 必须分割到两个不连通的部分中。
为了简化问题,我们直接把a点作为根,从a点开始做DFS。
开头想是不是 判断b的时间戳(dfn)是否不小于我们找到的割点u就可以?
很快就否定了,如下情况就不行:

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

#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;
}

浙公网安备 33010602011771号