首页 > 其他分享 >Prufer 序列

Prufer 序列

时间:2023-08-26 21:55:17浏览次数:34  
标签:Pr 步骤 fa fer 序列 Prufer

Prufer 序列实际上是一种转化的产物,这种转化使得一棵有 \(n\) 个点的无根树可以在线性时间内与一个有 \(n-2\) 个元素,且序列中元素权值在 \([1,n]\) 中的序列互相转化。

它与单纯的父节点组成的序列区别在于:对于每棵树,它的 Prufer 序列是唯一的,每一个 Prufer 序列也对应着唯一一棵树。换句话说,树与 Prufer 序列是双射关系。

Prufer 序列强大的地方就在这里。把一棵树转化为一个序列,而序列的计数难度明显小于树的计数。

由树转化成 Prüfer 序列

先给出转化的步骤:

  1. 寻找到当前最小的叶子节点 \(u\)。
  2. 把 \(u\) 的父亲加入 Prüfer 序列。
  3. 从树上删去 \(u\)。
  4. 重复前三个步骤,直到 Prüfer 序列有 \(n-2\) 个数。此时原树必定只剩下两个点。

一个显然的想法是使用小根堆,时间复杂度为 \(O(n\log n)\)。但是在这里我们有线性做法:

  1. 找到当前最小叶子节点 \(u\)。
  2. 把 \(u\) 的父亲 \(fa\) 加入 Prüfer 序列,并将 \(fa\) 的度数减少 \(1\)。
  3. 如果 \(fa\) 的度数变为 \(1\),且 \(fa<u\),那么将 \(fa\) 的父亲 \(h\) 加入 Prüfer 序列。因为此时 \(fa\) 必定为最小叶子(\(u\) 本来为最小叶子,但是 \(fa\) 比 \(u\) 还小。在删去 \(u\) 的过程中没有除 \(fa\) 以外的新叶子产生,所以 \(fa\) 为最小叶子)。
  4. 重复第三步,每次都跟 \(u\) 比较。直到有一个不符合条件。此时增加 \(u\)。
  5. 重复前四步,直到 Prüfer 序列有 \(n-2\) 个数,此时原树必定只剩下两个点。
for (int i = 1; i <= n; ++i) deg[fa[i]]++;

static int check[N], tot;

for (int i = 1; i <= n; ++i) if (deg[i] == 0) check[i] = 1;

for (int mn = 1; mn < n; ++mn) {

  int u = mn;

  while (check[u] == 1) {

	p[++tot] = fa[u];

	if (tot == n - 2) break;

	check[u] = 0;deg[fa[u]]--;

	if (deg[fa[u]] == 0) {

	  check[fa[u]] = 1;

	  if (mn > fa[u]) u = fa[u];

	}

  }

  if (tot == n - 2) break;

}

Prüfer 序列的性质

  1. Prufer 序列中每个点出现的次数代表它有几个儿子,即度数 \(+1\)。

由 Prüfer 序列还原树

先给出还原步骤:

  1. 找到当前最小的,不在序列中的点 \(u\),序列中的下标最小点 \(v\) 即为 \(u\) 的父亲。
  2. 删除序列中的第一个 \(v\)。
  3. 重复以上步骤,最后会留下最大点和一个点 \(u\)。在一般题目里我们不妨设最大点为无根树的假想根,那么最后的步骤是将 \(u\) 连向最大点。

类似于树转 Prüfer 序列,我们也有线性方法:

  1. 找到当前最小的,不在序列中的点 \(u\),序列中下标最小的点 \(v\) 即为 \(u\) 的父亲。
  2. 删除序列中的第一个 \(v\),如果 \(v\) 删除后不再在序列中出现,且 \(v<u\),那么 \(v\) 的父亲即为序列中下标最小的点的父亲。
  3. 重复步骤二,每次与 \(u\) 比较,直到有一个不符合条件。此时增加 \(u\)。
  4. 重复以上步骤,直到 Prüfer 序列为空。
for (int i = 1; i <= n - 2; ++i) deg[p[i]]++;

static int tot;

for (int mn = 1; mn < n; ++mn) {

  int u = mn;

  while (deg[u] == 0) {

	fa[u] = p[++tot];

	deg[u] = 1;

	if (tot == n - 2) break;

	deg[fa[u]]--;

	if (deg[fa[u]] == 0) if (mn > fa[u]) u = fa[u];

  }

  if (tot == n - 2) break;

}

for (int i = 1; i < n; ++i) if (fa[i] == 0) fa[i] = n;

标签:Pr,步骤,fa,fer,序列,Prufer
From: https://www.cnblogs.com/closureshop/p/17659529.html

相关文章

  • 801. 使序列递增的最小交换次数(状态机dp)
     dp的本质就是图论状态机dp就是包含多个待选状态,个人感觉就是分层图,每一层是一个状态,不同状态之间有可以相互转化的方法。通过状态和状态之间的关系,来实现状态转移。本题f[i][j]表示只从前i项中选,f[i][0]表示第i项不进行交换,f[i][1]表示第i项进行交换,达到严格递增情况下所需......
  • EF 多对多循环引用序列化失败 解决办法
    解决办法:外键添加[JsonIgnore]特性即可解决 ///<summary>///文章相册///</summary>[Table("ArticleAlbum")]publicclassArticleAlbumModel{///<summary>///主键ID///</summary>[Display(Name="主键ID")]......
  • Prüfer 序列
    用于解决带标号的生成树计数问题,一般用于计数问题。建立Prüfer序列重复下列操作\(n-2\)次,得到长度为\(n-2\)的Prüfer序列。取出编号最小的叶子节点\(x\),将与\(x\)相连的节点加入Prüfer序列中。将\(x\)和与\(x\)相连的边删去。明显的,每个点在Prüf......
  • 12.Acwing基础课第799题-简单-最长连续不重复子序列
    12.Acwing基础课第799题-简单-最长连续不重复子序列题目描述给定一个长度为n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。输入格式第一行包含整数n。第二行包含n个整数(均在0∼1050∼105范围内),表示整数序列。输出格式共一行,包含一个整数,表示最长的不......
  • P6604 [HNOI2016] 序列 加强版
    链接:P6604[HNOI2016]序列加强版首先,像这种题可以转化为计算贡献,即计算每一个元素成为最小值的次数。这个次数怎么求呢?显然单调栈模板,对于每一个数计算左边和右边第一个比它小的数\(l[i]\)和\(r[i]\)。CODE1:for(inti=1;i<=n;i++){ while(k&&a[i]<a[sta[k]]){ k--; ......
  • 例题两则(不无聊的子序列,HNOI2016序列)
    分享例题两则主要是分享一种\(\text{trick}\)。\(\text{UVA1608}\)题目描述给定一个长度为\(n\)的序列\(a\),如果\(a\)的每一个子串都存在至少一个元素只出现了一次,输出\(\text{Non-boring}\)。反之,输出\(\text{Boring}\)。\(n\leqslant2\times10^5\)。思路点......
  • 【LeetCode动态规划#15】最长公共子序列II
    最长公共子序列(二)描述给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列数据范围:0≤∣���1∣,∣���2∣≤20000≤∣str1∣,∣str2∣≤2000要求:空间复杂度�(�2)O(n2),时间复杂度�(�2)O(n2)......
  • 【剑指Offer】41、和为S的连续正数序列
    【剑指Offer】41、和为S的连续正数序列题目描述:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,......
  • 多元时间序列 | Matlab粒子群算法优化深度置信网络(PSO-DBN)多变量时间序列预测
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • java序列化
    序列化和反序列化序列化:把对象转换为字节序列的过程称为对象的序列化.反序列化:把字节序列恢复为对象的过程称为对象的反序列化.什么时候需要用到序列化和反序列化将内存中的对象持久化到磁盘、数据库或网络传输对象深拷贝Serializable接口在Java中实现了Serializab......