Prufer 序列
定义与建立
Prufer 序列可以将一个带标号 个结点的树用 中的 个整数表示。一个无向带标号生成树与数列之间的双射。
对于一棵树,每次我们选择它编号最小的叶子结点,删除它并记录下与它相连的节点的编号,那么最终记录下的 个数就组成了这棵树的 Prufer 序列。
显然用这个东西维护树的结构感觉非常不好,这个东西主要用来数数用的。
对树建立 Prufer 序列
按照定义直接建立,那么每次从堆中取出最小的数,删去它并记录。
这样可以得到一个 的做法,但是显然这不够优美,我们需要一个 的解法。
我们发现剩余的叶子结点数量是递减的,每次删除要么少一个,要么少一个又多一个。
从小到大枚举编号 ,如果是叶子结点,将和它们相邻的点放入 Prufer 序列中。考虑这个叶子结点带来的新叶子结点:
- 如果和它相邻的点编号 或者 没有变成叶子结点,那么不用管,它会在之后被枚举到。
- 但是如果 ,它在时候不会被枚举到了。但是发现删除之前当前最小的叶子结点是 ,所以删后 必然是最小的叶子,所以可以直接继续删除 。
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 序列中的每个数的出现次数就是原树中每个点的度数 ,可以每次连一个叶子结点重构。
方法类似,一个暴力的做法是每次选出最小的当前的叶子结点,将它与 Prufer 序列最前面的元素相连,这样复杂度是 的。
发现叶子结点编号递增,设删去的叶子结点为 ,和它相邻的是 。当加完 后 的度数变为 并且 ,那么它下一次一定被选择,直接继续连边即可。
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 序列与无根树一一对应(好像比较显然)
-
度数为 的点在 Prufer 序列中出现 次(上面还用到了这个结论)
-
【根据度数求方案】对于给定每个点度数为 的无根树,方案数为:
证明:Prufer 序列长度为 ,每个数出现次数为 ,根据组合意义直接计算全排列即可。
-
【根据连通块数量与大小求方案】一个 个点 条边的带标号无向图有 个连通块,每个连通块大小为 ,需要增加 条边使得整个图联通,方案数为:(但是当 时需要特判)
证明见 OI-wiki
本文作者:EricQian's Blog
本文链接:https://www.cnblogs.com/EricQian/p/15996222.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步