ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

prufer 序列小记

2021-09-11 19:31:19  阅读:246  来源: 互联网

标签:叶子 集合 序列 vdots prufer &- 小记


(prufer 序列仅对 \(n>1\) 有效,\(n=1\) 一般要特判)

prufer 序列是 \(n\) 个点的有标号无根树集合与 \(([1,n]\cap\Z)^{n-2}\) 的一种双射方式,可以将不会处理的树形结构转化为数组,在很多计数题里很有用。

下面先给出 prufer 序列的构造方式(即定义),然后给出通过任意长度为 \(n-2\) 每个元素 \(\in[1,n]\) 的序列作为 prufer 序列还原出存在唯一的树的方式(也就自然证明了双射性)。

从树构造 prufer 序列

一次操作指找到当前编号最小的叶子,将它唯一连向的点的编号 append 进 prufer 序列,并且删除该叶子。进行 \(n-2\) 次操作,还剩下两个由一条边相连的点,此时得到的长度为 \(n-2\) 的序列则为 prufer 序列。

注意到 \(n\) 永远不会成为被找到的叶子,所以可以钦定 \(n\) 为根,那么每次叶子唯一连向的点就是父亲,这样使实现更方便。考虑实时维护各节点的儿子个数,等于 \(0\) 则及时加入叶子集合,每次从叶子集合里找最小的,可以用堆简单地线对维护。

事实上有线性做法。注意到每次插入后必找最小值,那就可以看刚刚插入的是否最小值,如果是的话就直接用掉,否则才加入叶子集合。这样一来叶子集合的最小值是有单调性的,可以直接用桶存,然后对最小值「one-pointer」即可。

code:

void prf_init(){
	for(int i=1;i<n;i++)deg[fa[i]]++;
	for(int i=1;i<=n;i++)if(!deg[i])hav[i]++;
	int now=1,least=0,d;
	for(int i=1;i<=n-2;i++){
		if(least)d=least,least=0;
		else{while(!hav[now])now++;d=now;hav[now]--;}
		prf[i]=fa[d];
		if(!--deg[fa[d]])if(fa[d]<now)least=fa[d];else hav[fa[d]]++;
	}
}

从 prufer 序列还原树

通过 prufer 序列的定义易知 \(i\) 在 prufer 序列中出现的次数就等于 \(\deg_i-1\)(对 \(x\neq n\),\(x\) 的儿子全被删除了;对 \(x=n\),\(x\) 恰有一个儿子没被删除,但相比非根节点没有父亲对度数的贡献)。那么通过 prufer 序列就可以知道每个点的儿子个数,一开始也就能知道叶子集合。

考虑从左往右通过已知的叶子集合模拟 prufer 序列的构造过程。每次找到最小叶子、删除,可知当前最小叶子跟当前 prufer 值有一条边。如此 \(n-2\) 次之后连了 \(n-2\) 条边,还剩一条边是剩下两个点的边,其中一个是 \(n\),另一个就在 \([1,n)\) 里找一下谁还没爸爸。

通过叶子集合模拟的过程就跟上面构造 prufer 序列的线性做法一模一样地做即可。所以说构造和还原的代码其实基本一样。

code:

void fa_init(){
	for(int i=1;i<=n-2;i++)deg[prf[i]]++;
	for(int i=1;i<=n;i++)if(!deg[i])hav[i]++;
	int now=1,least=0,d;
	for(int i=1;i<=n-2;i++){
		if(least)d=least,least=0;
		else{while(!hav[now])now++;d=now;hav[now]--;}
		fa[d]=prf[i];
		if(!--deg[fa[d]])if(fa[d]<now)least=fa[d];else hav[fa[d]]++;
	}
	for(int i=1;i<n;i++)if(!fa[i])fa[i]=n;
}

mol ban tea & code

由此可得一个小结论(当然 prufer 序列的应用远不止于此):\(n>1\) 个点的有标号无根树数量为 \(n^{n-2}\)。其实用矩阵树定理也能证(只不过稍烦),就胡个证明吧(就当练习一下线代吧)。

其实就是无向图生成树个数,造出基尔霍夫矩阵,只要证 \(n-1\) 阶行列式

\[\begin{vmatrix}n-1&-1&-1&\cdots&-1\\-1&n-1&-1&\cdots&-1\\-1&-1&n-1&\cdots&-1\\\vdots&\vdots&\vdots&\ddots&-1\\-1&-1&-1&-1&n-1\end{vmatrix}=n^{n-2} \]

将第一行作为减数减到其他所有行上,设 \(x\) 阶方阵 \(K_x\) 为

\[\begin{bmatrix}n-1&-1&-1&\cdots&-1\\-n&n\\-n&&n\\\vdots&&&\ddots\\-n&&&&n\end{bmatrix} \]

只要证 \(|K_{n-1}|=n^{n-2}\)。事实上我们有 \(|K_x|=(n-x)n^{x-1}\)(这是证明不是推导/xyx),下面按 \(x\) 归纳证明之。

\(|K_1|=n-1\) 显然满足。当 \(x>1\) 时,将 \(K_x\) 按第二行展开:\((2,1)\) 余子式是个上三角矩阵,显然等于 \(-n^{x-2}\);\((2,2)\) 余子式显然是 \(|K_{x-1}|\);其他位置都是 \(0\),无贡献。假设 \(K_{x-1}\) 满足,有

\[\begin{aligned}|K_x|&=(-1)^{2+1}(-n)\!\left(-n^{x-2}\right)+(-1)^{2+2}n(n-x+1)n^{x-2}\\&=-n^{x-1}+(n-x+1)n^{x-1}\\&=(n-x)n^{x-1}\end{aligned} \]

证毕!

标签:叶子,集合,序列,vdots,prufer,&-,小记
来源: https://www.cnblogs.com/ycx-akioi/p/prufer.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有