基环树(环套树) 总结

基环树,也是环套树,是一种有 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] 骑士

基环树(环套树) 总结_第1张图片

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

#include 
#include 
#include 
#include 
#include 
#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);
	}
}
inline i

你可能感兴趣的:(模板总结,学习笔记,c++,算法,开发语言,图论)