首页 > 其他分享 >学习笔记:树与图上的计数问题

学习笔记:树与图上的计数问题

时间:2024-05-23 12:30:05浏览次数:12  
标签:标号 cur ++ 笔记 学习 int 序列 计数问题 节点

Prüfer 序列

\(n\) 个点的有标号无根树可以与一个长度为 \(n - 2\) 的 Prüfer 序列对应。

从树到 Prüfer 序列

  1. \(f\) 为空序列。
  2. 如果当前树上多于两个节点,假设当前标号最小的叶子为 \(x\),与 \(x\) 相连的节点标号为 \(y\),那么把 \(x\) 从树上删除,把 \(y\) 加入 \(f\) 末尾。
  3. 重复 2. 直到树上只有两个节点。

性质

  1. 在构造完 Prufer 序列后原树中会剩下两个节点,其中一个一定是编号最大的点 \(n\)。
  2. 每个节点在序列中出现的次数是其度数减 \(1\),因此没有出现的就是叶节点。

实现

可以 \(O(n)\) 实现这个转换。

由于最后一定会留下 \(n\),不妨钦定 \(n\) 为根(把 \(n\) 拎起来)。

定义:

  • \(f_i\) 为 \(i\) 的父节点。
  • \(d_i\) 为 \(i\) 的儿子数量。
  • \(p_i\) 为 prufer 序列的第 \(i\) 项。

流程:

定义 \(i\) 表示当前编号最小的叶子的指针,\(cur\) 为当前序列填到哪一项。

  • \(p_{cur} = f_i\)。
  • 将 \(d_{p_{cur}}\) 减 \(1\),表示删去 \(i\) 这个节点。
  • 如果 \(d_{p_{cur}} = 0\) 且 \(p_{cur} < i\),那么 \(p_{cur}\) 比当前最小值还要小,直接令 \(p_{cur + 1} = f_{p_{cur}}\) 。
void Tree_to_Prufer() {
	for(int i = 1; i < n; ++ i) {
		cin >> f[i];
		++ d[f[i]];
	}
	for(int cur = 0, i = 1; cur < n - 2; ++ i) {
		while(d[i]) ++ i;
		p[++ cur] = f[i];
		while(cur < n - 2 && -- d[p[cur]] == 0 && p[cur] < i) {
			p[cur + 1] = f[p[cur]];
			++ cur;
		}
	}
	for(int i = 1; i <= n - 2; ++ i) cout << p[i] << ' ';
}

从 Prüfer 序列到树

  1. 找到当前不在 \(f\) 中且还未使用的最小元素 \(x\)。
  2. \(x\) 与 \(f\) 的第一个元素连边。
  3. 删除 \(f\) 的第一个元素,如果 \(f\) 非空,重复上述过程。
  4. 最后还剩两个点未使用,将他们连边。

代码实现类似。

void Prufer_to_Tree() {
	for(int i = 1; i <= n - 2; ++ i) {
		cin >> p[i];
		++ d[p[i]];		// 以 $n$ 为根,儿子数量
	}
	p[n - 1] = n;
	for(int cur = 1, i = 1; cur <= n - 1; ++ cur, ++ i) {
		while(d[i]) ++ i;
		f[i] = p[cur];
		while(cur < n - 1 && -- d[p[cur]] == 0 && p[cur] < i) {
			f[p[cur]] = p[cur + 1];
			++ cur;
		}
	} 
	for(int i = 1; i <= n - 1; ++ i) cout << f[i] << ' ';
}

Caylay 定理

\(n\) 个点的有标号无根树有 \(n^{n -2}\) 种。

例题

P6086 【模板】Prufer 序列

submission

P2290 [HNOI2004] 树的计数

题意:给定每个点的度数 \(d_i\),求不同的有标号无根树个数。

相当于 \(d_i - 1\) 个 \(i\) 排进长为 \(n - 1\) 的序列的方案数。

\[\dfrac{(n - 1)!}{(d_1 - 1)!(d_2 - 1)!\cdots(d_n - 1)!} \]

由于保证了答案小于 \(10^{17}\),可以不用高精,全程用一个大于 \(10^{17}\) 的质数取模(如 \(4179340454199820289\))。

特判无解,根据 prufer 序列的性质,一定有 \(\sum d_i - 1 = n - 2\)。

submission

标签:标号,cur,++,笔记,学习,int,序列,计数问题,节点
From: https://www.cnblogs.com/Luxinze/p/18208159

相关文章

  • 机器学习-数学
    线性代数1、行列式本质数值性质性质一:交换行或者列,行列式要变号(正负)性质二:可以提取公因数性质三:倍数加(减),将某一行乘以任意数值加减到另一行(列),行列式不变性质四:拆分,将某一行(列)都是任意两个数值相加,可以拆分成两个行列式性......
  • QGIS开发笔记(二):Windows安装版二次开发环境搭建(上):安装OSGeo4W运行依赖其Qt的基础环境De
    前言  使用QGis的目的是进行二次开发,或者说是融入我们的应用(无人车、无人船、无人机),本片描述搭建QGis二次基础开发环境,由于实在是太长了,进行了分篇:上半部分:主要是安装好后,使用QtCreator可以使用QGIs的apps下的Qt使用对应的编译器编译不带qgis的空工程。下半部分:在上半......
  • shader 学习的好助手 --- chatgpt4-o
    其实要看懂godot官方或者第三方写的复杂效果的shader的代码,是比较难的第一:资料比较少,塔尖上的功能,分享的人少第二:大神也是慢慢熬成的第三:这类需求,在一个项目种少,大部分都是类似CURD 第四:shader的知识方向浩瀚如海,各种理论,各种高大上的公式,就考验的定力和耐心,就单单一......
  • U-BERT论文阅读笔记
    U-BERT:Pre-trainingUserRepresentationsforImprovedRecommendation论文阅读笔记Abstract学习用户表征是推荐系统的一项关键任务,因为它可以编码用户对个性化服务的偏好。用户表征一般是从点击互动和评论意见等行为数据中学习的。然而,对于不太流行的领域,行为数据不足以学习......
  • 深度学习-nlp-NLP之trainsformer位置编码与余弦距离--77
    目录1.位置编码与词嵌入2.余弦距离1.位置编码与词嵌入importtorchimporttorch.nnasnnimportmath#定义词向量嵌入的大小d_model=512#定义位置编码的维度max_seq_len=5000#定义词向量嵌入层embedding=nn.Embedding(vocab_size,d_model)#定义位置编......
  • .NetCore源码阅读笔记系列之Security (二) 自定义认证实践
    通过前面对AddCookie或者 AddOpenIdConnect等了解,其实里面都实现了一个AuthenticationHandler<TOptions>的认证处理,接下来我们来简单自定义一个试试首先我来实现下面这个方式,我添加了一个AddLIYOUMING()services.AddAuthentication(options=>{......
  • DeepMTS深度学习神经网络多元时间序列预测宏观经济数据可视化|附数据代码
    原文链接:https://tecdat.cn/?p=36237原文出处:拓端数据部落公众号在数据科学领域,时间序列分析一直是一个至关重要的研究方向,尤其在金融、气象、医学以及许多其他科学和工业领域中,准确的时间序列预测对于制定策略、政策规划以及资源管理都具有极其重要的意义。随着技术的不断进步,......
  • redis学习
    Redis简介数据库Redis数据存储在内存中(内存数据库),存储数据为Key-Value形式,单线程(指网络IO以及数据读写只由一个线程完成),其他功能例如持久化,异步删除,集群数据同步等是由额外的线程完成的,采用epoll异步IO多路复用,Redis所有操作均为原子操作,能够确保数据的一致性和完整性,广泛用于缓......
  • Ubuntu 22.04.4 深度学习环境配置
    显卡为NVIDIA4090D 显卡驱动安装成功后,输入以下命令,查看驱动支持最高的CUDA版本。nvidia-smi一、CUDA安装(1)官网下载对应CUDA(NvidiaCUDADownload / CUDAToolkitArchive|NVIDIADeveloper)以CUDA11.8为例(师兄用12.2也未冲突)  (2)驱动安装开网上推荐安装runfil......
  • 《Linux内核完全注释》学习笔记:2.4 Linux内核进程控制
    程序是一个可执行的文件,而进程(process)是一个执行中的程序实例。利用分时技术,在Linux操作系统上同时可以运行多个进程。分时技术的基本原理:把CPU的运行时间划分成一个个规定长度的时间片(timeslice),让每个进程在一个时间片内运行。当进程的时间片用完时系统就利用调度程序......