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");
 	}
posted @ 2022-09-06 10:38  浅渊  阅读(64)  评论(0)    收藏  举报