ABC266 F(基环树)
题目链接
题目大意:给\(n\)点\(n\)边的无向图,要判断\(u\rightarrow v\)是否只存在一条简单路径。
思路:
看到\(n点n边\)可以想到基环树,也就是在一棵树上多连一条边是的树上出现一个环,其余的点都是在以这个环上的点为根的子树中。那么不难想到如果\(u,v\)是在同一棵子树中,那么肯定是仅有一条简单路径到达。但是如果是在不同的子树中的话,必定存在两条简单路径从\(u\rightarrow v\),可以从环上的两边前往\(v\),所以只需要判断\(u,v\)是不是在同一个子树中就可以了。
首先是要找环,并且我们需要将这个环上的所有的点都记录下来,可以考虑用一个栈来将所有的节点都存到栈中,当找到了环之后,就将栈中所有的节点都打上标记。
int n;
std::cin >> n;
std::vector<std::vector<int>> adj(n + 1);
for (int i = 1; i <= n; i++) {
int u, v;
std::cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> stk(n + 10);
int idx = 0;
std::vector<bool> vis(n + 1), cir(n + 1);
bool flag = 0;
std::function<void(int, int)> dfs1 = [&] (int u, int fa) -> void {
if (!flag) stk[++idx] = u;
vis[u] = true;
for (auto& v : adj[u]) {
if (v == fa) continue;
if (!vis[v]) dfs1(v, u);
else if (!flag){
while(stk[idx] != v) {
cir[stk[idx--]] = true;
}
cir[v] = true;
flag = true;
}
}
if (!flag) idx--;
};
dfs1(1, 0);
要将所有判断两个节点是不是在同一个子树里面,就需要找到他们的根节点是哪一个,所以就先将环上节点的子树中的节点都连向根节点,考虑用并查集来实现。
std::vector<int> f(n + 1);
std::iota(f.begin(), f.end(), 0);
std::function<void(int, int, int)> dfs2 = [&] (int u, int fa, int top) -> void {
f[u] = top;
for (auto& v : adj[u]) {
if (v == fa) continue;
if (!cir[v]) dfs2(v, u, top);
}
};
for (int i = 1; i <= n; i++) {
if (cir[i]) dfs2(i, 0, i);
}
int q;
std::cin >> q;
for (int i = 1; i <= q; i++) {
int x, y;
std::cin >> x >> y;
std::cout << (f[x] != f[y] ? "No\n" : "Yes\n");
}

浙公网安备 33010602011771号