洛谷 P8655:发现环 ← 基环树模板题

【题目来源】
https://www.luogu.com.cn/problem/P8655

【题目描述】
小明的实验室有 N 台电脑,编号 1∼N。原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了 BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?

【输入格式】
第一行包含一个整数 N。
以下 N 行每行两个整数 a 和 b,表示 a 和 b 之间有一条数据链接相连。
输入保证合法。

【输出格式】
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。

【输入样例】
5
1 2
3 1
2 4
2 5
5 3

【输出样例】
1 2 3 5

【说明/提示】
对于 30% 的数据,1≤N≤1000。
对于 100% 的数据,1≤N≤10^5,1≤a,b≤N。
时限 1 秒, 256M。蓝桥杯 2017 年第八届国赛。

【算法分析】
● 众所周知,树上没有环。一棵树由 n 个结点及 n−1 条边构成。
● 基环树是由 n 个结点及 n 条边组成的
连通图

显然,基环树上存在环。因此,基环树本质上不是树,而是图。基环树又称章鱼图
基环树的的特别之处就在于这个环,因此,大部分基环树题目中,
找环是十分必要的。
(1)“简单图”找环代码

void getloop(int u) {
    dep[u]=dep[fa[u]]+1;
    for(auto t:e[u]) {
        if(t==fa[u]) continue;
        if(!dep[t]) {
            fa[t]=u;
            getloop(t);
        } else if(dep[t]<dep[u]) {
            int i=u;
            while(1) {
                st[i]=1;
                //loop[++idx]=i;
                if(i==t) break;
                i=fa[i];
            }
        }
    }
}

(2)“重边图”找环代码

void getloop(int u,int v) {
    dep[u]=dep[fa[u]]+1;
    for(auto t:e[u]) {
        int fi=t.first,se=t.second; 
        if(se==v) continue;
        if(!dep[fi]) {
            fa[fi]=u;
            getloop(fi,se);
        } else if(dep[fi]<dep[u]) {
            int i=u;
            while(1) {
                st[i]=1;
                //loop[++idx]=i;
                if(fi==v) break;
                i=fa[i];
            }
        }
    }
}

● 有向基环树
有向基环树分为
内向基环树外向基环树

内向基环树,每个结点出度为 1;外向基环树,每个结点入度为 1。
●​​​​​​​ 基环树森林
多棵树可以组成一个森林,多棵基环树可以组成基环树森林。
多棵外向基环树可以组成
外向基环树森林,多棵内向基环树可以组成内向基环树森林。​​​​​​​

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int dep[N],fa[N];
bool st[N];
vector<int> e[N];
int x,y;
int n;

void getloop(int u) {
    dep[u]=dep[fa[u]]+1;
    for(auto t:e[u]) {
        if(t==fa[u]) continue;
        if(!dep[t]) {
            fa[t]=u;
            getloop(t);
        } else if(dep[t]<dep[u]) {
            int i=u;
            while(1) {
                st[i]=1;
                if(i==t) break;
                i=fa[i];
            }
        }
    }
}

int main() {
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>x>>y;
        e[x].push_back(y); //emplace_back
        e[y].push_back(x);
    }

    getloop(1);

    for(int i=1; i<=n; i++) {
        if(st[i]) cout<<i<<" ";
    }

    return 0;
}

/*
in:
5
1 2
3 1
2 4
2 5
5 3

out:
1 2 3 5
*/



【参考文献】
https://www.cnblogs.com/pure4knowledge/p/18211724
https://www.cnblogs.com/yifan0305/p/17506679.html
https://blog.csdn.net/ZhuRanCheng/article/details/131736222



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值