Hoping for the best but expectin|

EricQian06

园龄:4年6个月粉丝:24关注:43

2022-03-12 09:09阅读: 59评论: 0推荐: 0

Prufer 序列

定义与建立

Prufer 序列可以将一个带标号 n 个结点的树用 [1,n] 中的 n2 个整数表示。一个无向带标号生成树与数列之间的双射。

对于一棵树,每次我们选择它编号最小叶子结点,删除它并记录下与它相连的节点的编号,那么最终记录下的 n2 个数就组成了这棵树的 Prufer 序列。

显然用这个东西维护树的结构感觉非常不好,这个东西主要用来数数用的。

对树建立 Prufer 序列

按照定义直接建立,那么每次从堆中取出最小的数,删去它并记录。

这样可以得到一个 O(nlogn) 的做法,但是显然这不够优美,我们需要一个 O(n) 的解法。

我们发现剩余的叶子结点数量是递减的,每次删除要么少一个,要么少一个又多一个。

从小到大枚举编号 p,如果是叶子结点,将和它们相邻的点放入 Prufer 序列中。考虑这个叶子结点带来的新叶子结点:

  • 如果和它相邻的点编号 p>p 或者 p 没有变成叶子结点,那么不用管,它会在之后被枚举到。
  • 但是如果 p<p,它在时候不会被枚举到了。但是发现删除之前当前最小的叶子结点是 p,所以删后 p 必然是最小的叶子,所以可以直接继续删除 p
for(int i=1,x;i<n;i++) x=rd(),add(i,x),add(x,i),ind[i]++,ind[x]++;
// [1,n-1] 的父亲序列 
for(int i=1;i<=n;i++) if(ind[i]==1) exist[i]=true;
for(int i=1,p;i<=n;i++)
{
	 if(!exist[i]) continue;
	 p=i;
	 while(p<=i)
	 {
	 	 used[p]=true;
	 	 for(int j=hea[p];j;j=nex[j])
	 	 	 if(!used[ver[j]]) { p=ver[j]; break; }
	 	 if(ind[p]==1) break;
	 	 ind[p]--,pru[++cnt]=p;
	 	 if(ind[p]==1) exist[p]=true;
	 	 if(ind[p]!=1 || p>i) break;
	 }
	 if(ind[p]==1) exist[p]=true;
}
assert(cnt==n-2);
for(int i=1;i<=cnt;i++) ret^=1ll*i*pru[i];
printf("%lld\n",ret);

用 Prufer 还原无根树

发现 Prufer 序列中的每个数的出现次数就是原树中每个点的度数 1,可以每次连一个叶子结点重构。

方法类似,一个暴力的做法是每次选出最小的当前的叶子结点,将它与 Prufer 序列最前面的元素相连,这样复杂度是 O(nlogn) 的。

发现叶子结点编号递增,设删去的叶子结点为 p,和它相邻的是 p。当加完 pp 的度数变为 1 并且 p<p,那么它下一次一定被选择,直接继续连边即可。

for(int i=1;i<=n-2;i++) pru[i]=rd(),ind[pru[i]]++;
for(int i=1;i<=n;i++) if(!ind[i]) exist[i]=true;
int pos=1;
for(int i=1,p;i<=n;i++)
{
	 if(!exist[i]) continue;
	 p=i;
	 while(p<=i)
	 {
	 	 if(pos==n-1) { fa[p]=n; break; }
	 	 fa[p]=pru[pos],p=pru[pos],ind[p]--,pos++;
	 	 if(pos>=n) break;
	 	 if(ind[p] || p>i) break;
	 }
	 if(!ind[p]) exist[p]=true;
}
for(int i=1;i<n;i++) ret^=1ll*i*fa[i];
printf("%lld\n",ret);

主要性质

  • Prufer 序列与无根树一一对应(好像比较显然)

  • 度数为 di 的点在 Prufer 序列中出现 di1 次(上面还用到了这个结论)

  • 【根据度数求方案】对于给定每个点度数为 di 的无根树,方案数为:

    (n2)!i=1n(di1)!

    证明:Prufer 序列长度为 n2,每个数出现次数为 di1,根据组合意义直接计算全排列即可。

  • 【根据连通块数量与大小求方案】一个 n 个点 m 条边的带标号无向图有 k 个连通块,每个连通块大小为 si,需要增加 k1 条边使得整个图联通,方案数为:(但是当 k=1 时需要特判)

    nk2i=1ksi

    证明见 OI-wiki

本文作者:EricQian's Blog

本文链接:https://www.cnblogs.com/EricQian/p/15996222.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   EricQian06  阅读(59)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起