P6086 【模板】Prufer 序列

前言:

\(prufer\)序列常用于树的计数问题,一些常用的性质:
1.度数为\(d\)的点,在\(prufer\)序列出现的次数为\(d-1\)次。
2.对于完全图的生成树为\(n^{n-2}\)

题意:

两种操作:
\(1.给定一棵树转换成prufer序列。\)
\(2.给定prufer序列重构一棵树。\)

题解:

1.如何把一颗树转换成\(prufer\)序列?
一棵树有着唯一的\(prufer\)序列与之对应,\(n\)个节点的树,\(prufer\)序列的长度为\(n-2\),重复两个步骤:1.找出最小编号的度数为\(1\)的节点将它的父亲节点放入\(prufer\)序列中。2.将该节点删除。重复这两个步骤,直到这棵树只剩下两个节点,此时的序列就是\(prufer\)序列。这个过程有两种写法:
(1).用小根堆维护度数为\(1\)的节点的编号,如果一个点的父亲节点度数为\(1\),则扔进堆里面,时间复杂度\(O(nlogn)\)
(2).线性维护:先找出节点中序号最小的且度数为\(1\)的节点,从这个节点开始,如果他的父亲节点为\(1\)过后,且该节点的父亲节点的编号小于当前节点,那么就从该节点开始继续否则,按照顺序继续找。时间复杂度\(O(n)\)

	for(int i=1;i<n;i++)	scanf("%d",&f[i]);
	for(int i=1;i<=n;i++)	de[i]++;
	for(int i=1;i<n;i++)	de[f[i]]++;
	for(int i=1;i<=n;i++)
	if(de[i]==1){pos=i;break;}
	leaf=pos;
	while(cnt<n-2){
		int tmp=f[leaf];
		de[tmp]--;p[++cnt]=tmp;
		if(de[tmp]==1&&tmp<pos){
			leaf=tmp;
		}else{
			pos++;
			while(de[pos]!=1)	pos++;
			leaf=pos;
		}
	}

2.如何将\(prufer\)序列重构成一棵树?
借鉴前面的方法,先算出每个节点的度数,同样先找出节点编号最小的度数为\(1\)的点,该点的父亲节点是\(prufer\)的第一个点,如果度数为\(1\),那么父亲的父亲就是第二个点,否则继续找下一个度数为1的编号,继续就可以了。

	for(int i=1;i<=n-2;i++)	scanf("%d",&p[i]);
	for(int i=1;i<=n;i++)	de[i]=1;
	for(int i=1;i<=n-2;i++)	de[p[i]]++;
	for(int i=1;i<=n;i++){
		if(de[i]==1){
			pos=i;break;
		}
	}
	leaf=pos;
	while(cnt<n-2){
		int fa;
		fa=f[leaf]=p[++cnt];
		de[fa]--;
		if(de[fa]==1&&fa<pos)	leaf=fa;
		else{
			pos++;
			while(de[pos]!=1)	pos++;
			leaf=pos;
		}
	}
	f[leaf]=n;
最后的代码:
#include "bits/stdc++.h"
using namespace std;
const int N=5e6+5;
int n,m,cnt,pos,leaf,f[N],p[N],de[N];
long long ans;
void solve1(){
	for(int i=1;i<n;i++)	scanf("%d",&f[i]);
	for(int i=1;i<=n;i++)	de[i]++;
	for(int i=1;i<n;i++)	de[f[i]]++;
	for(int i=1;i<=n;i++)
	if(de[i]==1){pos=i;break;}
	leaf=pos;
	while(cnt<n-2){
		int tmp=f[leaf];
		de[tmp]--;p[++cnt]=tmp;
		if(de[tmp]==1&&tmp<pos){
			leaf=tmp;
		}else{
			pos++;
			while(de[pos]!=1)	pos++;
			leaf=pos;
		}
	}
	for(int i=1;i<=cnt;i++)	ans^=1ll*i*p[i];
	printf("%lld",ans);
}
void solve2(){
	for(int i=1;i<=n-2;i++)	scanf("%d",&p[i]);
	for(int i=1;i<=n;i++)	de[i]=1;
	for(int i=1;i<=n-2;i++)	de[p[i]]++;
	for(int i=1;i<=n;i++){
		if(de[i]==1){
			pos=i;break;
		}
	}
	leaf=pos;
	while(cnt<n-2){
		int fa;
		fa=f[leaf]=p[++cnt];
		de[fa]--;
		if(de[fa]==1&&fa<pos)	leaf=fa;
		else{
			pos++;
			while(de[pos]!=1)	pos++;
			leaf=pos;
		}
	}
	f[leaf]=n;
	for(int i=1;i<=n;i++)	ans^=1ll*i*f[i];
	printf("%lld",ans);
}
int main(){
	scanf("%d %d",&n,&m);
	if(m==1)	solve1();
	if(m==2)	solve2();
	
	
	return 0;
}
posted @ 2021-02-15 20:03  喜欢李知恩IU  阅读(84)  评论(0)    收藏  举报