基环树(环套树) 总结

基环树是一种特殊的有 n 个点 n 条边的图,形式上表现为环上每个点都有一个子树。文章介绍了基环树的两种类型:内向树和外向树,并探讨了如何在有向图和无向图中找环。通过实例解析了如何处理基环树问题,包括洛谷 P2607 和 P5022 题目,以及解决直径问题的优化算法。文章提供了详细的解题思路和代码实现。
摘要由CSDN通过智能技术生成

基环树,也是环套树,是一种有 n 个点 n 条边的图,简单地讲就是树上在加一条边。它形如一个环,环上每个点都有一棵子树的形式。

基环内向树:每个点出度为1(因此每个环上点的子树,儿子指向父亲)

基环外向树:每个点入度为1(因此每个环上点的子树,父亲指向儿子)

基环树的关键就是找到环,可以先把环当作这个无根树的 “根” ,也就是把环当成一个点(先不管它),这样一颗基环树就变成了一个普通的树,然后我们先按照解决普通树的方法对“根”的所有子树依次处理求解答案,最后在单独对环上所有的点进行操作求解最终答案即可。

有向图找环

inline void find(int u,int rt){
	vis[u] = 1;
	for(re int i(head[u]) ; i ; i=e[i].nxt){
		int v = e[i].to;
		if(v == rt) { r1=u; r2=v; return; } //找到了环,设为这条边的两个端点
		if(vis[v]) continue;
		find(v,rt);
	}
}

无向图找环

inline void find(int u,int rt){
    vis[u] = 1;
    for(re int i(head[u]) ; i ; i=e[i].nxt){
        int v = e[i].to;
        if(v == rt) continue;
        if(vis[v]){
            fl = 1;
            r1 = u;
            r2 = v;
            return;
        }
        find(v,u);
        if(fl) return;
    }
}

来看例题

洛谷 P2607 [ZJOI2008] 骑士

这道题可以算是一个例题吧,考虑如何进行连边。由于一个人可能被多个人痛恨,那么把一个人痛恨的人作为这个人的父亲节点进行连边。这样这个图就变成了一个有向的基环树。我们每一次找环,把环断开成链,设断开的边的两个端点是 u 和 v,分别对 u 进行 dfs 和 v 进行 dfs,取两个的最大值。由于相邻的两个点最多只能选其中一个,那么这道题断开环之后就变成了一个基础的树形DP。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define int long long
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int M = 2e6+10;
int n;
int head[M],w[M],vis[M],f[M][2];
int cnt,sum,res1,res2,r1,r2;
struct edge{
	int to,nxt;
}e[M];
inline void add(int u,int v){
	e[++cnt].to = v;
	e[cnt].nxt = head[u];
	head[u] = cnt;
}
inline void find(int u,int rt){ //找环
	vis[u] = 1;
	for(re int i(head[u]) ; i ; i=e[i].nxt){
		int v = e[i].to;
		if(v == rt) { r1=u; r2=v; return; }
		if(vis[v]) continue;
		find(v,rt);
	}
}
inli
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要找出基环树的环c,可以使用深度优先搜索(DFS)算法来实现。以下是使用vector存储图的一个示例代码: ```cpp #include <iostream> #include <vector> using namespace std; vector<bool> visited; // 记录节点是否已经访问过 vector<int> parent; // 记录每个节点的父节点 vector<bool> inCycle; // 记录节点是否在环中 vector<int> cycle; // 存储找到的环 // 深度优先搜索 bool dfs(vector<vector<int>>& graph, int node, int parentNode) { visited[node] = true; parent[node] = parentNode; for (int neighbor : graph[node]) { if (!visited[neighbor]) { if (dfs(graph, neighbor, node)) return true; } else if (neighbor != parentNode) { // 找到了环 cycle.push_back(neighbor); inCycle[neighbor] = true; int cur = node; while (cur != neighbor) { cycle.push_back(cur); inCycle[cur] = true; cur = parent[cur]; } cycle.push_back(neighbor); // 将最后一个节点加入环中 return true; } } return false; } // 找到基环树的环c vector<int> findCycle(vector<vector<int>>& graph) { int n = graph.size(); visited.resize(n, false); parent.resize(n, -1); inCycle.resize(n, false); for (int i = 0; i < n; i++) { if (!visited[i] && dfs(graph, i, -1)) break; } return cycle; } int main() { int n = 4; // 图的节点数 vector<vector<int>> graph(n); // 添加图的边 graph[0].push_back(1); graph[1].push_back(0); graph[1].push_back(2); graph[2].push_back(1); graph[2].push_back(3); graph[3].push_back(2); vector<int> cycle = findCycle(graph); // 输出环c中的节点 cout << "环c中的节点: "; for (int node : cycle) { cout << node << " "; } cout << endl; return 0; } ``` 以上代码通过深度优先搜索遍历图,当遇到一个已经访问过的节点时,如果该节点不是当前节点的父节点,则找到了一个环。然后通过记录每个节点的父节点,可以回溯找到构成该环的所有节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值