prufer序列

/*
prufer序列
prufer序列其实就是将带有标号的n个节点(即每个节点不等价)的树用[1,n]的n-2个数唯一表示
即n个点的完全图的生成树与长度为n-2的prufer序列构成一一对应的关系 
所以根据prufer序列,我们可以知道,完全图生成树的个数为n^(n-2) 
*/
#include <iostream>
using namespace std;

typedef long long ll;
const int maxn = 5e6+5;

int f[maxn],p[maxn],d[maxn];

void solve1(int n)    
//将无根树转化为prufer序列,因为n这个节点在这一过程中不会被删去
//所以为了方便编码,我们以n为根来构造prufer序列
//a[i]表示当n为根时i的父亲节点 
//找到编号最小的叶子节点,将它的父亲节点作为当前的prufer序列的值,然后删去这个节点,直到只剩两个节点 
//剩两个节点时,prufer序列的第n-1项一定是n,所以只需要n-2项就可以唯一表示一棵无根树
//如果是有根树,最后两个节点也一定有n,那么第n-1项一定是根,因为这里是无根树,所以我们直接用n做根 
{
	for (int i = 1; i < n; i++)
	{
		cin >> f[i];
		d[f[i]] ++;   
	}
	int j = 1;
	for (int i = 1; i <= n-2; i++)
	{
		while( d[j] ) j++;   //找编号最小的叶子节点 
		p[i] = f[j];
		d[p[i]] --;
		while( i < n-2 && d[p[i]] == 0 && p[i] < j )  
		//删去j点后,当p[i]这个点为叶子时,它比j小,那么下次就一定是选它了 
		{
			p[i+1] = f[p[i]];
			d[p[i+1]] --;
			i ++;
		}
		j ++;   //这个节点用过了 
	}
	ll ans = 0;
	for (int i = 1; i <= n-2; i++)
	{
		//cout << p[i] << '\n'; 
		ans ^= (ll)i*p[i];
	}
	cout << ans << '\n';
}
void solve2(int n)
//在原树中度为x的点,在prufer序列中出现了x-1次 
//将prufer序列转化为有根树,遍历prufer序列 
//找到编号最小的叶子节点,prufer序列中没有出现的一定为叶子节点
//那么这个叶子节点的父亲就是prufer序列的当前项 
{
	for (int i = 1; i <= n-2; i++) 
	{
		cin >> p[i];
		d[p[i]] ++;
	}
	p[n-1] = n;   //注意prufer序列是以根为n构造出来的,所以第n-1项一定是n
	int j = 1; 
	for (int i = 1; i <= n-1; i++)
	{
		while( d[j] ) j++;
		f[j] = p[i];
		d[p[i]] --;
		while( i < n-1 && d[p[i]] == 0 && p[i] < j )
		{
			f[p[i]] = p[i+1];
			d[p[i+1]] --;
			i ++;
		}
		j ++; 
	}
	ll ans = 0;
	for (int i = 1; i < n; i++)
	{
		ans ^= (ll)i*f[i];
	}
	cout << ans;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n,m;
	cin >> n >> m;
	if( m == 1 ) solve1(n);
	else solve2(n);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值