Prufer 序列 学习笔记

定义

Prufer 序列的定义:
对于一个有根树,每次选择编号最小的叶子节点删掉,然后记录他的父亲,直到剩下两个节点,形成一个 \(n-2\) 长度的序列。
可以证明这个序列和一个 \(n\) 个节点的无根树一一对应。同时也可以证明一个 \(n\) 个节点完全图的生成树数量为 \(n^{n-2}\)

由树构造 Prufer 序列

显然用堆可以 \(\Theta(n\log n)\)
发现如果一个节点删除之后,如果他的父亲成为了新的叶子节点,并且编号比删除的节点小,那么接下来删除的肯定是他的父亲(因为其他的节点编号都比删除的节点大。这样就线性了。\(\Theta(n)\)

由 Prufer 序列构造树

根据 Prufer 序列可以得到树的度数。用堆依旧是 \(\Theta(n\log n)\)
考虑模仿树构造 Prufer 序列,如果序列中后一个数字比前一个数字更小,那么肯定就是父子关系,否则继续找最小的度数是 \(1\) 的节点,同样是线性的。

P6086 【模板】Prufer 序列

int n,opt,fa[maxn],p[maxn],d[maxn]; 
void S1(){//tree -> prufer 
	int i,j; ll ans=0; for(i=1;i<n;i++) fa[i]=read(),d[fa[i]]++;
	for(i=1,j=1;i<=n-2;i++,j++){
		while(d[j]) j++; p[i]=fa[j];
		while(i<=n-2&&!--d[p[i]]&&p[i]<j) p[i+1]=fa[p[i]],i++;
	} for(i=1;i<=n-2;i++) ans^=1ll*i*p[i]; print(ans); return;
}
void S2(){//prufer -> tree
	int i,j; ll ans=0; for(i=1;i<=n-2;i++) p[i]=read(),d[p[i]]++; p[n-1]=n;
	for(i=1,j=1;i<n;i++,j++){
		while(d[j]) j++; fa[j]=p[i];
		while(i<n&&!--d[p[i]]&&p[i]<j) fa[p[i]]=p[i+1],i++;
	} for(i=1;i<n;i++) ans^=1ll*i*fa[i]; print(ans); return;
}
int main(){
	freopen("1.in","r",stdin);
	//freopen(".out","w",stdout);
	n=read(); opt=read(); if(opt==1) S1(); else S2(); return 0;
}
posted @ 2023-03-16 20:29  jiangtaizhe001  阅读(14)  评论(0编辑  收藏  举报