基环树
定义
一个图包含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;
}

浙公网安备 33010602011771号