首页 > 其他分享 >「学习笔记」基环树

「学习笔记」基环树

时间:2023-06-27 20:57:11浏览次数:34  
标签:dep ch fa int 笔记 学习 基环树 getloop read

众所周知,一棵有 \(n\) 个节点的树有 \(n - 1\) 条边,树上没有环。
据此,明显的,对于一个有 \(n\) 个结点 \(n\) 条边的无向连通图,必定是在一棵树上的任意两个节点之间连一条边构成的。我们把 \(n\) 个节点 \(n\) 条边的无向连通图,就称为基环树。

基环树上存在环,因此基环树它不是树,而是图。基环树又称章鱼图。

image

如图,这就是一棵基环树。
如果一张有向弱连通图每个点的入度都为 \(1\),则称它是一棵 基环外向树,如下图。

image

如果一张有向弱连通图每个点的出度都为 \(1\),则称它是一棵 基环内向树,如下图。

image

多棵树可以组成一个森林,多棵基环树可以组成基环森林,多棵基环外向树可以组成基环外向树森林,多棵基环内向树可以组成基环内向森林。

找环

基环树的的特别之处就在于这个环,因此,大部分基环树题目中,找环是十分必要的。
简单图中,可以利用 dfs 的天然栈性质来找环,具体实现方式与 tarjan 求强连通分量很相似。

void getloop(int u) {
	dep[u] = dep[fa[u]] + 1;
	for (int v : e[u]) {
		if (v == fa[u])	continue ;
		if (!dep[v]) {
			fa[v] = u;
			getloop(v);
		}
		else if (dep[v] < dep[u]) {
			for (int i = u; ; i = fa[i]) {
				ok[i] = 1;
				loop[++ m] = i;
				if (i == v) {
					break;
				}
			}
		}
	}
}

再有重边的图中,需要做一下小小的处理,与 tarjan 求双连通分量很像,记录一下边的编号,只要不走回刚走过来的边就行。

vector<pair<int, int> > e[N];

void getloop(int u, int i) {
	dep[u] = dep[fa[u]] + 1;
	for (auto it : e[u]) {
		int v = it.first, k = it.second;
		if (k == i)	continue;
		if (!dep[v]) {
			fa[v] = u;
			getloop(v, k);
		}
		else if (dep[v] < dep[u]) {
			for (int j = u; ; j = fa[j]) {
				ok[j] = 1;
				loop[++ m] = j;
				if (i == v) {
					break;
				}
			}
		}
	}
}

练习

基环树题目中,较常见的 套路 思路就是先将环去掉,对每一棵树做处理,最后再对环做处理。

P8943 Deception Point

这是一道基础的入门题。
我们发现,只要两个人能在到达环上之前不碰面(包括刚到达环上),那雷切尔就一定存活 秦王绕柱,否则就必死无疑,处理三角洲到达环上的距离与雷切尔到达环上的距离,再查看是否会在到达环上是碰面即可。

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 2e5 + 5;

int n, q, m;
int dep[N], top[N], fa[N], loop[N], dis[N];
bool ok[N];
vector<int> e[N];

void getloop(int u) {
	dep[u] = dep[fa[u]] + 1;
	for (int v : e[u]) {
		if (v == fa[u])	continue ;
		if (!dep[v]) {
			fa[v] = u;
			getloop(v);
		}
		else if (dep[v] < dep[u]) {
			for (int i = u; ; i = fa[i]) {
				ok[i] = 1;
				loop[++ m] = i;
				if (i == v)	break;
			}
		}
	}
}

void dfs(int u, int fat, int tp) {
	top[u] = tp;
	for (int v : e[u]) {
		if (v == fat || ok[v])	continue ;
		dep[v] = dep[u] + 1;
		dfs(v, u, tp);
	}
}

int main() {
	n = read<int>(), q = read<int>();
	for (int i = 1, x, y; i <= n; ++ i) {
		x = read<int>(), y = read<int>();
		e[x].emplace_back(y);
		e[y].emplace_back(x);
	}
	getloop(1);
	memset(dep, 0, sizeof dep);
	for (int i = 1; i <= m; ++ i) {
		dfs(loop[i], 0, i);
//		cout << loop[i] << ' ';
	}
	while (q --) {
		int x = read<int>(), y = read<int>();
		int d1 = dep[x], t1 = top[x];
		int d2 = dep[y], t2 = top[y];
		if (d2 + min(abs(t1 - t2), m - abs(t1 - t2)) > d1) {
			puts("Survive");
		}
		else {
			puts("Deception");
		}
	}
	return 0;
}

P8655 [蓝桥杯 2017 国 B] 发现环

简单的找环模板 = =||

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;

int n;
int dep[N], fa[N];
bool ok[N];
vector<int> e[N];

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

int main() {
	n = read<int>();
	for (int i = 1, x, y; i <= n; ++ i) {
		x = read<int>(), y = read<int>();
		e[x].emplace_back(y);
		e[y].emplace_back(x);
	}
	getloop(1);
	for (int i = 1; i <= n; ++ i) {
		if (ok[i]) {
			printf("%d ", i);
		}
	}
	return 0;
}

P6037 Ryoku 的探索

朴素做法为 \(O_{n^2}\) 的。
但仔细观察,就会发现,最终答案一定是除去环上一条边,其他边的长度总和,删掉哪条边呢?根据美观度来判断。

/*
  The code was written by yifan, and yifan is neutral!!!
 */

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

template<typename T>
inline T read() {
	T x = 0;
	bool fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 1e6 + 5;

int n, m;
ll ans, sum;
int loop[N], top[N], fa[N], dep[N];
ll dis[N];
bool ok[N];

struct node {
	int v;
	ll w, p;
	
	node(int a, ll b, ll c): v(a), w(b), p(c) {};
};

vector<node> e[N];

void getloop(int u) {
	dep[u] = dep[fa[u]] + 1;
	for (node it : e[u]) {
		int v = it.v;
		if (v == fa[u])	continue;
		if (!dep[v]) {
			fa[v] = u;
			getloop(v);
		}
		else if (dep[v] < dep[u]) {
			for (int i = u; ; i = fa[i]) {
				ok[i] = 1;
				loop[++ m] = i;
				if (i == v)	break;
			}
		}
	}
}

void dfs(int u, int fat, int tp) {
	top[u] = tp;
	for (node it : e[u]) {
		int v = it.v;
		ll w = it.w;
		if (v == fat || ok[v])	continue;
		ans += w;
		dfs(v, u, tp);
	}
}

int main() {
	n = read<int>();
	for (int i = 1, x, y; i <= n; ++ i) {
		x = read<int>(), y = read<int>();
		ll w = read<ll>(), q = read<ll>();
		e[x].emplace_back(y, w, q);
		e[y].emplace_back(x, w, q);
	}
	getloop(1);
	for (int i = 1; i <= m; ++ i) {
		dfs(loop[i], 0, loop[i]);
	}
	loop[m + 1] = loop[1];
	for (int i = 1; i <= m; ++ i) {
		for (node it : e[loop[i]]) {
			int v = it.v;
			if (v == loop[i + 1]) {
				dis[i] = it.w;
				sum += it.w;
				break;
			}
		}
	}
	for (int i = 1, u; i <= n; ++ i) {
		u = top[i];
		ll W = 0, P = 1e9;
		for (node it : e[u]) {
			int v = it.v;
			ll w = it.w, p = it.p;
			if (!ok[v])	continue;
			if (p < P) {
				W = w;
				P = p;
			}
		}
		printf("%lld\n", sum - W + ans);
	}
	return 0;
}

标签:dep,ch,fa,int,笔记,学习,基环树,getloop,read
From: https://www.cnblogs.com/yifan0305/p/17506679.html

相关文章

  • 机器学习.周志华《12 计算学习理论 》
     基础知识计算学习理论(computationallearningtheory)是通过“计算”来研究机器“学习“的理论,其目的是分析学习任务的困难本质。例如:在什么条件下可进行有效的学习,需要多少训练样本能获得较好的精度等,从而为机器学习算法提供理论保证。几个基本概念回顾:泛化误差:学习器在总体上的......
  • 莫比乌斯反演 学习笔记
    炫酷反演魔术!莫反会用到的具体性质证明先不写,先写题。与其说是学习笔记,不如说是简要的题解集合。不太想贴太多代码啊,翻起来很烦。P3455[POI2007]ZAP-Queries很基础的一道题。令\(a\leb\),考虑\(k=1\)的情况。\[\begin{aligned}ans&=\sum\limits_{i=1}^a\sum\limits_{j=......
  • 嘿 Siri,有没有「三天速成深度学习」的课程?
    「嘿Siri,有没有三天速成NLP的课程?」「很抱歉,我没有找到任何和『有没有三天速成NLP的课程?』有关的内容。」最近老能在朋友圈刷到「X 天学会Python,从此大专吊打博士生,工资蹭蹭往上升,抛弃Excel不是梦」。我们的耐心,正在变得越来越短。前段时间,外卖骑手配送时间从系统中消失的......
  • 火遍日本 IT 界的「鱼书」终出续作,原来进阶深度学习竟然那么简单
    在日本,有一本书在AI领域的影响力超越了实力派的“花书”,长期位列日亚“人工智能”类图书榜首,众多五星好评。它被众多高校名师为AI入门教材,如果你也是AI领域的开发者,说不定你手上的这本书已经翻烂了。这就是被称为「鱼书」的《深度学习入门:基于Python的理论与实现》原书上市......
  • 机器学习 | TF-IDF详解
    什么是TF-IDFTF-IDF是一种常用的文本处理技术,用以评估一个词对于一篇文章或语料库中一篇文章的重要性。TF代表词频(TermFrequency),IDF代表逆文档频率(InverseDocumentFrequency)。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下......
  • 微服务架构基本原理学习笔记(三)
    上一篇:微服务架构基本原理学习笔记(二)五、微服务之间的通信微服务通信模式微服务本身并没有规定通信规则,换句话说,一个微服务并没有规定可以被哪些应用程序访问,或者被哪些其它的微服务调用。应用程序与微服务间的直接通信,或者微服务与微服务间的直接调用,往往会因为其中错综复......
  • 新书上市 | 数学不好,Python不行,还能入门机器学习吗?
    没错,图灵君又来安利好书了!什么书?机器学习?机器学习的书已经很多了,这本有啥特别的吗?当然有。话说有位日本网友,买了40多本数学和机器学习相关的书,愣是没有学会,直到遇到了这本,那叫一个相见恨晚呐!嗯,你没猜错,就是一本引进日本的书。图灵的老朋友都知道,我们出版了很多日系好书,比如用图搞定......
  • 【深入浅出Docker原理及实战】「Docker安装说明」零基础+全方位带你学习探索Docker容
    安装DockerDocker中的容器是一种轻量级的虚拟化技术,它基于镜像运行并具有自己的状态。下面是Docker容器的安装操作。Docker有三种更新频道:stable、test和nightly。官方网站提供了各种环境下的安装指南,主要包括Linux、Windows10和macOS。这里我们侧重点去介绍和分析说明对应......
  • 想理解深度学习,究竟应该降维打击 or 升维思考?
    题图|DesignedbyFreepik让我们从一道选择题开始今天的话题。什么是神经网络?请选择以下描述正确的一项或多项。A.神经网络是一种数学函数,它接收输入并产生输出。B.神经网络是一种计算图,多维数组流经其中。C.神经网络由层组成,每层都具有「神经元」。D.神经网络是一种通用函数......
  • 菜鸟学习Spring——SpringMVC注解版解析不同格式的JSON串
    一、概述    不同格式的JSON串传到后台来实现功能这个是我们经常要做的一件事,本篇博客就给大家介绍四种不同的JSON串传到后台后台如何用@RequestBody解析这些不同格式的JSON串的。二、代码展示需要引用的jar包1.xml配置  Web.xml1.<?xmlversion="1.0"encoding="UTF-8......