基环树完全入门

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44824275/article/details/93719776

基环树

基环树的概念

\qquad 众所周知,N个点的树有N-1条边.若在树上任意添加一条边,则会形成一个环,除了环之外,其余部分由若干棵子树构成。
\qquad 我们把这种N个点N条边的连通无向图,即在树上加一条边构成的恰好包含一个环的图,称为“基环树”。如果不保证连通,那么N个点N条边的无向图也可能是若干棵基环树组成的森林,简称为“基环树森林“。
\qquad 在有向图中,我们也有类似的概念。N个点、N条边、每个节点有且仅有一条入边的有向图就好像以“基环”为中心,有向外扩展的趋势,故称为“外向树”。N个点、N条边、每个节点有且仅有一条出边的有向连通图就好像以“基环”为中心,有向内收缩的趋势,故称为“内向树”。外向树和内向树也经常统称为“基环树”。如果不保证连通,那么N个点、N条边、每个节点有且仅有一条出(入)边的有向图也可能是“内(外)向树森林”的形态。

在这里插入图片描述
基环树(N个点N条边的连通无向图)
在这里插入图片描述
外向树(每个点的出度都为1,进了环就出不去)
在这里插入图片描述
内向树(每个点的入度都为1,出了环就回不来)

基环树相关问题

1)基本思路

\qquad 基环树的结构仍然很简单,但比树要复杂些,因此常作为一些经典模型的扩展,以适当增加题目的难度,例如基环树的ﻪ直径、基环树两点之间的距离、基环树动态规划等。无论哪种模型,求解基环树相关问题的方法一般都是先找出图中唯一的环,把环作为基环树的广义“根节点”,把除了环之外的部分按照若干棵树处理,再考虑与环一起计算。1

2)基环树的直径
3)基环树DP

经典例题

1)NOIP2018 旅行(拆边+贪心)

\qquad 对于前 50 % 50\% 的数据,因为是一棵树,所以你一旦访问了某个子节点,那么你就必须把这颗子树访问完才能访问其他子节点,否则剩余的节点将无法访问到。

\qquad 要求字典序最小,那么我们肯定先从 1 1 出发,然后对于每一个节点,我们肯定是按照字典序的大小依次访问,看一眼良心的数据范围,我就暴力地把所有子节点存下来,排序一遍依次访问(数组尽量不要开在递归里,爆栈我不负责)。
然后记录一下输出就好了。

\qquad 50 % 50\% 的数据是基环树,对于一个环,假设有 m m 条边,那么无论如何旅行,有且仅有 m 1 m-1 条边能被访问,也就是说,旅行的路径仍旧是一棵树。也就是每次暴力的割断环上的每一条边,然后跑一遍,找一个字典序最小的就好了。找环可以用 t a r j a n tarjan 。也可以把所有的边都存下来,挨个枚举,暴力拆边。2

\qquad 洛谷传送门

//代码框架来自luogu_chen_zhe
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

inline long long read()
inline void write(long long x)  ///篇幅有限,读入/输出优化函数省略

const int kmax=5010,INF=0x3f3f3f3f;
vector<int> graph[kmax];
int n,m,ans[kmax],edge[kmax][2];

namespace solve1           ///树上求解
{
    int cnt=0;             ///ans数组尾指针
    bool vis[kmax];        ///dfs标记

    inline void dfs(int u) ///大法师不解释
    {
        ans[++cnt]=u;
        vis[u]=true;
        int l=graph[u].size();
        for(int i=0;i<l;i++)
        {
            int v=graph[u][i];
            if(!vis[v]) dfs(v);
        }
    }

    void main()
    {
        cnt=0;         ///给每条边所连的点排序,保证贪心最优性
        for(int i=1;i<=n;i++) sort(graph[i].begin(),graph[i].end());
        dfs(1);
    }
}

namespace solve2       ///基环树上求解
{
    int cnt=0;         ///计数器
    int res[kmax];     ///存储结果
    int del_u,del_v;   ///要删除的边的起点与终点
    bool vis[kmax];    ///dfs标记

    inline bool comp() ///字典序比较
    {
        for(int i=1;i<=n;i++)
        {
            if(ans[i]!=res[i]) return ans[i]>res[i];
        }
        return false;
    }

    inline bool check(int u,int v)  ///如果是所删边,dfs不搜
    {
        if((u==del_u&&v==del_v)||(u==del_v&&v==del_u)) return false;
        return true;
    }

    inline void dfs(int u)          ///大法师
    {
        res[++cnt]=u;
        vis[u]=true;
        int l=graph[u].size();
        for(int i=0;i<l;i++)
        {
            int v=graph[u][i];
            if(!vis[v]&&check(u,v)) dfs(v);
        }
    }

    void main()
    {
        ans[1]=INF;              ///将ans字典序置为最大,保证其能被res更新
        for(int i=1;i<=n;i++) sort(graph[i].begin(),graph[i].end());
        for(int i=1;i<=m;i++)
        {
            cnt=0;
            memset(res,0,sizeof(res));
            memset(vis,0,sizeof(vis));
            del_u=edge[i][0];    ///枚举要删除边的起点和终点
            del_v=edge[i][1];
            dfs(1);
            if(comp()&&cnt==n) memcpy(ans,res,sizeof(res)); ///res更新ans
        }
    }
}

int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        graph[u].push_back(v);   ///graph存图
        graph[v].push_back(u);
        edge[i][0]=u;            ///edge存边
        edge[i][1]=v;
    }
    if(m==n-1) solve1::main();   ///判定为树
    else solve2::main();         ///判定为基环树
    for(int i=1;i<=n;i++)
    {
        write(ans[i]);
        putchar(' ');
    }
    putchar('\n');
    return 0;
}

2)ZJOI2008 骑士(拆边+树形DP)

\qquad 洛谷传送门

3)IOI2008 / BZOJ1791 岛屿(基环树直径)

【未完待续】

浅谈仙人掌

U p d a t e   2019.7.18 : Update \ 2019.7.18: 修正了一个错误,并用 G r a p h   E d i t o r Graph\ Editor 优化了样例可视化处理


  1. 李煜东《算法竞赛进阶指南》[M] 郑州:河南电子音像出版社 2018:357 ↩︎

  2. 引自洛谷——可口可乐头 ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_44824275/article/details/93719776