码迷,mamicode.com
首页 > 其他好文 > 详细

浅谈Prufer序列

时间:2020-06-21 18:13:00      阅读:67      评论:0      收藏:0      [点我收藏+]

标签:产生   代码   The   c++   its   read   通过   复杂度   problem   

\(\text{Prufer}\)序列,是树与序列的一种双射。

构建过程:

  • 每次找到一个编号最小的叶子节点\(Leaf\),将它删掉,并将它所连接的点的度数\(-1\),且加入\(\text{Prufer}\)序列。

  • 重复上述步骤,直到只剩下两个点。

实现:

考虑如何实现。

最朴素的显然每次暴力找,复杂度\(O(n^2).\)显然不够优秀。

用堆来维护节点,显然可以做到\(O(n\log n).\)

考虑线性实现:维护一个指针,每次指向编号最小的叶节点。删除之后,如果新产生了叶节点并且编号要比指针编号小,则加入\(\text{Prufer}\)序列,继续删除。否则自增到下一个叶节点。

指针只会增加,最多增加\(O(n)次\),时间复杂度即为\(O(n).\)

Rebuild重建

若已知\(\text{Prufer}\)序列,如何重建树?

先通过\(\text{Prufer}\)序列将每一个元素的度\((\text{Deg})\)还原。

同样地,维护一个指针,每次指向编号最小的叶子节点,并把它与当前\(\text{Prufer}\)序列的第一个元素相连。将两个元素的度都减一,并继续找新产生的\(Leaf\),同\(\text{Prufer}\)序列的构建过程。

时间复杂度同样是\(O(n).\)

用处:

  • 凯莱定理:\(n\)个点的完全图生成树个数为\(n^{n-2}.\)
  • 用来造树的数据
  • 推一堆计数式子

例题

代码实现:

下面是模板代码,用了\(\text{fread.}\)

#include<bits/stdc++.h>
using namespace std;
int n,m,f[5000010],p[5000010],d[5000010];
long long ans;
namespace IO{
	char buf[1<<21],*p1=buf,*p2=buf;
	#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
	inline int read(){
		int s=0,w=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch==‘-‘)w=-1;
			ch=gc();
		}
		while(isdigit(ch)){
			s=s*10+ch-‘0‘;
			ch=gc();
		}
		return w==-1?-s:s;
	}
}
using namespace IO;
inline void Turn_Prufer(){
	for(int i=1;i<n;++i)f[i]=read(),++d[f[i]];
	//j为指针 
	for(int i=1,j=1;i<n-1;++i,++j){
		while(d[j])++j;p[i]=f[j];//找到第一个deg为0的点,即为叶子,删掉,把它相连的点加到Prufer里 
		while(i<n-1&&!--d[p[i]]&&p[i]<j)p[i+1]=f[p[i]],++i;//如果Prufer还没找完,并且删掉它爹(这里新产生的点就是p[i])的度数后也成为叶子,且它的编号要小,此处的p[i]就是上一个点(j)的爹 
	}
	for(int i=1;i<n-1;++i)ans^=1ll*i*p[i];
}
inline void Turn_Father(){
	for(int i=1;i<n-1;++i)p[i]=read(),++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]]&&p[i]<j)f[p[i]]=p[i+1],++i;//p[i]就是新产生的点,根据Prufer逆向构造即可 
	}
	for(int i=1;i<n;++i)ans^=1ll*i*f[i];
}
inline void write(long long A){
	if(A<0)putchar(‘-‘),A=-A;
	if(A>9)write(A/10);
	putchar(A%10+‘0‘);
}
int main(){
	n=read(),m=read();
	if(m==1)Turn_Prufer();
	else Turn_Father();
	write(ans);
	return 0;
}

浅谈Prufer序列

标签:产生   代码   The   c++   its   read   通过   复杂度   problem   

原文地址:https://www.cnblogs.com/h-lka/p/13173203.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!