基环树

定义

一个图包含n个点n条边,且图中只存在一个环,这样的图被称为基环树
基环树可以看作以环点为根的一颗颗子树构成

三类

1.无向树
2.外向树(每个点只有一条入边)

环上的根节点与子树的连边指向外部
可以理解为,若u的入边为v到u,v是u的父节点

3.内向树(每个点只有一条出边)

子树中的边指向环上的根节点
可以理解为,若u的出边为u到v,v是u的父节点

思路

1.深搜找环
2.断环成树,对树深搜计算

来个稍微简单一点的例子[ZJOI2008] 骑士

方法一:

建有向图,找环,断环,依次对断的两个点分别跑P1352(没有上司的舞会)这道题的树形DP

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=1000010;
int n;
int a[N],fa[N];
int r1,r2;
bool vis[N];
vector<int> g[N];
void find(int s,int rt){//找环 
	vis[s]=1;
	for(auto to:g[s]){
		if(to==rt){
			r1=s,r2=to;//找到环上相邻的两个点,分别作为树根进行DP 
			return;
		}
		if(vis[to]) continue;//打过标记说明to这个点不在这颗基环树的环上
		//因为在环上的话,这遍find会找到当前的环,并且会给这个图上所有点打上标记 
		find(to,rt);
	}
}
int dp[N][2];
void dfs(int s,int rt){
	dp[s][1]=a[s],dp[s][0]=0;
	for(auto to:g[s]){
		if(to==rt) continue;//把根的父节点与它的连线断掉 
		dfs(to,rt);
		dp[s][0]+=max(dp[to][1],dp[to][0]);
		dp[s][1]+=dp[to][0];
	}
}
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>fa[i];
		g[fa[i]].push_back(i);//每个点只有一个入边,基环外向树 
        //也就说明了,不在环上的点不会走到环上,只会往外走
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			r1=0,r2=0;
			find(i,i);
			if(r1){
				dfs(r1,r1);
				int res1=dp[r1][0];
				dfs(r2,r2);
				int res2=dp[r2][0];
				ans+=max(res1,res2);
			} 
		}
	}
	cout<<ans;
	return 0;
}

方法二:

建无向边,处理过程类似,注意一下建图细节,具体见代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1000010;
int n; 
int a[N];
struct Line{
	int v,id;
};
int fa[N];
vector<Line> g[N];
bool vis[N];
bool cut[N];
int rt1,rt2;
bool find(int s,int inl){
	vis[s]=1;
	for(auto to:g[s]){
		if(to.id==inl) continue;
		if(!vis[to.v]){//没去过这个点,就走进去
			if(find(to.v,to.id)) return 1;
		}
		else{//再次碰到一个点说明这个点是进入环的入点
			rt1=to.v; rt2=s;
			cut[to.id]=1;
			return 1;
		}
	}
	return 0; 
}
int dp[N][2];
void dfs(int s,int inl){
	vis[s]=true;
	dp[s][1]=a[s],dp[s][0]=0;
	for(auto to:g[s]){
		if(to.id==inl||cut[to.id]) continue;
		dfs(to.v,to.id);
		dp[s][0]+=max(dp[to.v][1],dp[to.v][0]);
		dp[s][1]+=dp[to.v][0];
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>fa[i];
		g[i].push_back((Line){fa[i],i});
		g[fa[i]].push_back((Line){i,i});
	} 
	int ans=0;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			rt1=rt2=0;
			if(find(i,0)){
				dfs(rt1,0);
				int res1=dp[rt1][0];
				dfs(rt2,0);
				int res2=dp[rt2][0];
				ans+=max(res1,res2);
			}
		}
	}
	cout<<ans<<'\n';
	return 0;
}

完结

posted @ 2025-07-15 08:37  hapihapi  阅读(77)  评论(0)    收藏  举报