2419. prufer序列

题目链接

2419. prufer序列

本题需要你实现prufer序列与无根树之间的相互转化。

假设本题涉及的无根树共有 n 个节点,编号 1n

为了更加简单明了的描述无根树的结构,我们不妨在输入和输出时将该无根树描述为一个以 n 号节点为根的有根树。

这样就可以设这棵无根树的父亲序列f1,f2,,fn1,其中 fi 表示将该树看作以 n 号节点为根的有根树时,i 号节点的父节点编号。

同时,设这棵无根树的prufer序列p1,p2,,pn2

现在,给定一棵由 n 个节点构成的无根树,以及该无根树的一个序列(父亲序列或prufer序列),请你求出另一个序列。

输入格式

输入共两行。

第一行包含两个整数 n,m,表示无根树的节点数以及给定序列类型。

如果 m=1,则第二行包含 n1 个整数,表示给定序列为父亲序列。

如果 m=2,则第二行包含 n2 个整数,表示给定序列为prufer序列。

输出格式

共一行,输出另一个序列,整数之间用单个空格隔开。

数据范围

2n105

输入样例1:

6 1 3 5 4 5 6

输出样例1:

3 5 4 5

输入样例2:

6 2 3 5 4 5

输出样例2:

3 5 4 5 6

解题思路

prufer编码

prufer编码主要作用即将一棵无根树转化为一个序列(即prufer序列),另外prufer序列也可以反过来转化为一棵树,即prufer序列和树之间是一一对应的,常用来解决一些证明问题,如凯莱定理等
证明凯莱定理(一个无向完全图有 nn2 棵生成树):由于prufer序列和树之间是一一对应的关系,证明有多少棵不同的生成树即证明有多少种prufer序列,显然,prufer序列共有 n2 项,其范围为 1n,故其种类数为 nn2

prufer编码的流程:假定 n 号节点为根,找到除根外度数最小的节点,在删除该节点之前,将其父节点输出,重复该流程,直到最后只剩下两个节点,即prufer序列只有 n2 个元素,因为prufer序列最多 n1 个元素,而最后一个元素一定为 n,所以这个元素可以省略,输出的元素即为prufer序列
线prufer
假定当前出度为 0 且编号最小的节点为 j,则输出 f[j],删除 j 之后,出度为 0 的节点至多只会增加一个,即 f[j],判断删除 j 之后 f[j] 的出度是否为 0,如果 f[j] 的出度为 0f[j]<j 说明 f[j] 是当前出度为 0 且编号最小的节点,递归输出这样的父节点即可,否则说明这样的 j 只会更大,即 j 只会增加,这样即可线性时间内将一颗树转化为prufer序列

prufer
先将 n 这个节点加入到prufer序列中,不难发现,prufer序列中某个数出现的次数即为该数在树中的儿子节点的数量,从 1 开始找到儿子数量为 0 且编号最小的节点 j,其父节点即为当前遍历的prufer序列的元素,将该元素从prufer序列中删去,因为删除该元素后儿子数量为 0 的节点数量至多直接增加一个,如果该元素的儿子数量为 0 且编号小于 j,说明当前节点即为儿子数量为 0 且编号最小的节点,递归处理即可,这样的 j 同样也是递增的,故可以在线性时间内将一个prufer序列转化为一棵树

  • 时间复杂度:O(n)

代码

// Problem: prufer序列 // Contest: AcWing // URL: https://www.acwing.com/problem/content/2421/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e5+5; int n,m; int f[N],p[N],d[N]; void tree2prufer() { for(int i=1;i<n;i++) { scanf("%d",&f[i]); d[f[i]]++; } for(int i=0,j=1;i<n-2;j++) { while(d[j])j++; p[i++]=f[j]; while(i<n-2&&--d[p[i-1]]==0&&p[i-1]<j)p[i++]=f[p[i-1]]; } for(int i=0;i<n-2;i++)printf("%d ",p[i]); } void prufer2tree() { for(int i=1;i<=n-2;i++) { scanf("%d",&p[i]); d[p[i]]++; } p[n-1]=n; for(int i=1,j=1;i<n;i++,j++) { while(d[j])j++; f[j]=p[i]; while(i<n&&--d[p[i]]==0&&p[i]<j)f[p[i]]=p[i+1],i++; } for(int i=1;i<n;i++)printf("%d ",f[i]); } int main() { scanf("%d%d",&n,&m); if(m==1)tree2prufer(); else prufer2tree(); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16951582.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示