Prüfer 序列学习笔记

Prüfer 序列是一种将带标号的树用一个唯一的整数序列表示的方法。


Prüfer 序列的构造

每次选择树上标号最小的叶子节点,把它连接的节点编号加入序列中,并且把它删掉。这样重复 \(n-2\) 次后剩下两个相连节点,生成的序列就是 Prüfer 序列。

使用堆的 \(O(n\log n)\) 构造方法是显然的。考虑线性构造过程。

记录指针 \(now\) 指向当前编号最小的叶子节点,设 \(l\) 为当前将要删掉的叶子,初始时 \(l=now\)

删除 \(l\) 之后,

  • 如果它连接的节点(设为 \(tmp\))变成新的最小叶子,那么令 \(l=tmp\)
  • 否则让 \(now\) 自增,直到找到新的最小叶子。

显然这样做是对的,而且时间复杂度是 \(O(n)\) 的。

用 Prüfer 还原树也可以用类似以上的方法用 \(O(n)\) 的时间实现。

模板题

int main()
{
	n=read();op=read();
	if(op==1)
	{
		for(int i=1;i<n;i++)fa[i]=read(),++ind[fa[i]],++ind[i];
		for(int i=1;i<=n;i++)
		{
			if(ind[i]==1)
			{
				now=i;break;
			}
		}
		l=now;
		for(int i=1;i<=n-2;i++)
		{
			int tmp=fa[l];--ind[l];ans^=1ll*i*fa[l];
			if(--ind[tmp]==1&&tmp<now)
			{
				l=tmp;
			}
			else
			{
				++now;
				while(ind[now]!=1)++now;
				l=now;
			}
		}
	}
	else
	{
		for(int i=1;i<=n-2;i++)p[i]=read(),++ind[p[i]];
		for(int i=1;i<=n;i++)++ind[i];
		for(int i=1;i<=n;i++)
		{
			if(ind[i]==1)
			{
				now=i;break;
			}
		}
		l=now;
		for(int i=1;i<=n-2;i++)
		{
			fa[l]=p[i];--ind[l];int tmp=fa[l];
			if(--ind[tmp]==1&&tmp<now)
			{
				l=tmp;
			}
			else
			{
				++now;
				while(ind[now]!=1)++now;
				l=now;
			}
		}
		fa[l]=n;
		for(int i=1;i<n;i++)ans^=1ll*i*fa[i];
	}
	write(ans);
	return 0;
}

Prüfer 序列的性质与应用

  • 每个节点在序列中出现的次数是其度数 \(-1\)
  • 每个长度为 \(n-2\),值域为 \([1,n]\) 的序列和一棵有 \(n\) 个点的无根树一一对应。

Cayley 公式

\(n\) 个点的完全图有 \(n^{n-2}\) 棵生成树。

图连通方案数

一个 \(n\) 个点的带标号无向图被分成了 \(k\) 个连通块,每个连通块有 \(s_i\) 个节点,求加最少的边使得图连通的方案数。

答案是 \(n^{k-2}\sdot \prod s_i\)证明.


例题

[HNOI2008] 明明的烦恼

因为有 Prüfer 序列与树一一对应的性质,只需要对 Prüfer 序列计数。非常简单,但是需要写高精度。

BZOJ4766 文艺计算姬

给定一张有 \(n\) 个左部点,\(m\) 个右部点的带标号完全二分图,计算其生成树个数。

考虑对 Prüfer 序列计数。最后两个点一定一个是左部点一个是右部点,剩下的点在序列中一定是左部点出现了 \(m-1\) 次,右部点出现了 \(n-1\) 次,答案就是 \(n^{m-1}m^{n-1}\)

「雅礼集训 2017 Day8」共

考虑求有 \(k\) 个左部点,\(n-k\) 个右部点的完全二分图的生成树个数。需要用上面那个结论。答案是 \(k^{n-k-1}n-k^{k-1}\binom{n-1}{k-1}\)

[THUPC 2018] 城市地铁规划

显然是先把所有度数的价值算出来然后做一个背包之类的。还要记录方案。

	for(int i=1;i<=n-2;i++)
	{
		for(int j=0;j<i;j++)
			if(g[i]<g[j]+f[i-j+1]-f[1])
			{
				g[i]=g[j]+f[i-j+1]-f[1];
				from[i]=j;
			}
	}

[ARC106F]Figures

首先枚举每个节点实际度数 \(d_i\),再计算选孔方案。

有一个式子:

\[\frac{(n-2)!}{\prod(d_i-1)!}\prod\frac{a_i!}{(a_i-d_i)!} \]

一顿化简之后得到:

\[(n-2)!\prod a_i\binom{\sum(a_i-1)}{n-2} \]

然后就做完了。

posted @ 2025-06-20 10:58  baiguifan  阅读(30)  评论(1)    收藏  举报