返回顶部

基环树

基环树入门

基环树是只有一个环的连通图,它有 nn 个点和 nn 条边。基环树不是一棵树,而是一棵“伪树”。它的特征是图中有且只有一个连通的环。下面分别讨论无向图和有向图两种情况下的基环树。

(1)无向图上的基环树。在一棵基于无向图的无根树上加一条边,形成基环树。去掉环上任意一条边,基环树变成一棵真正的树。

(2) 有向图上的基环树。一个有向无环图(DAG) , 如果在图中加一条边能形成一个自连通的环,则形成一棵基环树。把这个环看作一个整体,根据它与环外点的关系,把基环树分成两种:内向树,环外的点只能进人环内;外向树,环外的点无法进入环内。

图示:基环树的三种形态和缩点图

 

 

 

图 (a) ~ 图 (c) 是基环树的3种形态,把环缩成一个“虚点”后得到图 (d) ~ 图 (f) 。

请注意,缩成虚点后的无向图 (d) 是一棵树,但是缩成虚点后的有向图 (e) 和图 (f) 不一定是真正的树,如图 (e) 就不是一棵树。

由基环树的特征可知,与基环树有关的题目,首先是找到唯一的环,然后把这个环当作“虚点”。

由前面的无向图的连通性和有向图的连通性可知,基环树的找环问题是“图的连通性”的一个简化问题。

对于无向图, 用拓扑排序的 BFS 可以找出环,操作结束后, 度大于 11 的点就是环上的点。

具体做法:① 计算所有点的度;② 把所有度为11的点入队;③ 从队列弹出度为11的点,把它所连的边去掉,并将边所连的邻居点的度减11,若这个邻居的度变为11,人队;④ 继续执行步骤 ③ 直到队列为空。操作结束后,统计所有点,度数大于 11的点即为环上的点。

注意,这种无向图找环的方法只适用于只有一个环的基环树。

如果只要找环上的一个点, 用 DFS 可以方便地找到。如果有一个点 vv 第 22 次被访问到,那么就存在环,且 vv 是环上的一个点。这个方法可用于有向图和无向图。

 

 

例题:

洛谷 P2607 [ZJOI2008] 骑士 :传送锚点

 

题目描述

Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z 国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z 国又怎能抵挡的住 Y 国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

为了描述战斗力,我们将骑士按照 1 至 n 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

输入格式

第一行包含一个整数 n,描述骑士团的人数。

接下来 n 行,每行两个整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。

输出格式

应输出一行,包含一个整数,表示你所选出的骑士军团的战斗力。

样例

输入数据 1

3
10 2
20 3
30 1

输出数据 1

30

数据规模与约定

对于 30% 的测试数据,满足 n10;

对于 60% 的测试数据,满足 n100;

对于 80% 的测试数据,满足 n104

对于 100% 的测试数据,满足 1n106,每名骑士的战斗力都是不大于 106 的正整数。

 

【算法分析】

本题和“洛谷 P1352-没有上司的舞会”很相似。

洛谷 P1352 例题是一棵树,而本题生成的图是基环树森林。

把 x 讨厌的人 y 设为 x 的父节点,形成从 y 指向 x 的有向边。

本题每个点的入度为 1 ,生成的图中包括很多独立的连通块,每个连通块肯定是一棵基环树,而且形状是一棵外向树。

这些基环树形成了基环树森林。在每棵基环树上找到环,断开这个环后,这棵基环树变成了一棵真正的树,就可以套用洛谷 P1352 的做法了。

本题属于“基环树+树上DP”,下面给出代码。

其中的 DP 代码和洛谷 P1352 一样,这里不再解释。

基环树部分有以下两处。

(1) 找基环树环上一个点。用 check() 函数实现,它是一个 DFS,如果发现某个点被第 2 次访问,它就是环上的一个点,用 bj 记录这个点。

(2) 分别断开 bj 和 bj 的父节点,形成两棵树,在这两棵树上分别做 DP ,取最大值。

 

Code:

复制代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e6+5;
int n,bj,tot;
int fa[N],val[N];
int fi[N],ne[N],to[N];
ll ans;
ll dp[N][2];
bool vis[N];

void add(int x,int y)
{
    ne[++tot]=fi[x];
    fi[x]=tot;
    to[tot]=y;
    fa[y]=x;
}

void dfs(int x)
{
    dp[x][0]=0,dp[x][1]=val[x],vis[x]=1;
    for(int i=fi[x];i;i=ne[i])
    {
        int v=to[i];
        if(v==bj) continue;
        dfs(v);
        dp[x][1]+=dp[v][0];
        dp[x][0]+=max(dp[v][0],dp[v][1]);
    }
}

int check(ll x)
{
    vis[x]=1;
    return vis[fa[x]]?fa[x]:check(fa[x]);
}

ll solve(int x)
{
    ll s=0;
    bj=check(x);
    dfs(bj);
    s=max(s,dp[bj][0]);
    bj=fa[bj];
    dfs(bj);
    s=max(s,dp[bj][0]);
    return s;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>val[i]>>x;
        add(x,i);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i])
            ans+=solve(i);
    cout<<ans<<'\n';
}
复制代码


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   光暗之影x  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示