Prüfer 序列

Prüfer 序列

Prüfer 序列是将一颗 \(n\) 个有标号的点用一个长度为 \(n-2\) 的序列的表示的方法。

对于一颗有标号的树,会存在唯一一个 Prüfer 序列与之对应。一个 Prüfer 序列也只会对应一颗树。

将一颗树转化为 Prüfer 序列

首先对于所有的叶子节点(此时为 \(1,2,4,6\)),选择其编号最小的节点删掉。然后将其父亲节点加入 Prüfer 序列中。

在重复经过 \(n-2\) 次操作后,剩余了 \(2\) 个点,构造结束。

过程

对于一颗有标号的树:

图片取自 oi wiki

显而易见地是,1. 可以用一个小根堆来维护所有的叶子节点。

  1. 每次取出堆中最小的元素删除。

  2. 在每次删除节点后,判断它的父亲节点是否成为叶子节点,如果是,加入堆中。

  3. 重复操作,直至还剩 \(2\) 个节点。

for(int i=1;i<n;i++) {
	cin>>u>>v;
    addedge(u,v); addedge(v,u);
	d[u]++; d[v]++;
}
for(int i=1;i<=n;i++) {
    if(d[i]==1) q.push(i);
}
for(int i=1;i<=n-2;i++) {
	int u=q.top(), v; q.pop();
    vis[u]=true;
    for(int k=hd[u];i;i=nxt[i]) {
        if(!vis[to[i]]) v=to[i];
    }
	if(--d[v]==1) q.push(v);
}
for(int i=1;i<=n-2;i++) cout<<p[i]<<' ';

这样的做法的时间复杂度是 \(\text O(n\log_2n)\) 的。

线性做法 & 过程

通过维护一个指针 \(p\),初始时 \(p\) 指向编号最小的叶子节点,然后

  1. \(p\) 指向的节点删掉。判断与这个节点相连的节点是否成为叶子节点。
  2. 如果是,判断该节点的编号 \(x\) 是否大于 \(p\) 所指向的节点编号。如果大于,不做操作,否则,将节点 \(x\) 删掉,并判断与这个节点相连的节点是否成为叶子节点。重复操作 \(2\) 直至无法操作。
  3. 将指针 \(p\) 自增,直至到一个未被删掉的节点。

性质

可以发现,在 Prüfer 序列中,每个点 \(u\) 的出现次数是其点的度数 \(d_u-1\)

  1. 对于叶子节点,一定不会存在 Prufer序列中。

  2. 对于非叶子节点 \(u\),在节点 \(u\) 被删掉前,会有与之相连的 \(d_u-1\) 的节点会被删掉(其原因是在构造 Prufer序列的过程中树是联通的,且树大小始终不小于 \(2\)),在这个过程中,节点 \(u\) 就会被加入 Prufer序列 \(d_u-1\) 次。

由此也可以知道 \(\sum_{i=1}^{n}(d_u-1)=n-2\)

  1. 剩下的 \(2\) 个节点中,其中一个是编号最大的节点,且它绝对不会计入叶子节点中。

Prüfer 序列将转化为一颗树

过程

考虑将上述转化的过程,重复一遍。

  1. 对于 Prüfer 序列的第一个数 \(u\) ,和它相连的节点 \(v\) 一定是叶子节点中编号最小的。将 \(u\)\(v\) 连边。

  2. 那么维护一个叶子节点集合 \(S\),将这个编号最小的节点 \(v\) 删掉。

  3. 此时将 Prüfer 序列的第一个数也删掉,由于叶子节点在 Prüfer 序列的出现次数为 \(d-1=0\),那么如果这个数在 Prüfer 序列中的出现次数变为 \(0\) 的话,节点 \(u\) 变为叶子节点,加入集合 \(S\) 中。

  4. 重复该过程 \(n-2\) 次。

  5. 最终在集合 \(S\) 中会存在 \(1\) 个节点,将其和最大节点连接即可。

for(int i=1;i<=n-2;i++) {
    cin>>p[i]; d[p[i]]++;
}
for(int i=1;i<=n;i++) {
    if(!d[i]) q.push(i);
}
for(int i=1;i<=n-2;i++) {
	int u=q.top(); q.pop();
	addedge(p[i],u); addedge(u,p[i]);
	if(--d[p[i]]==0) q.push(p[i]);
}
addedge(n,q.top()); addedge(q.top(),n);

这个过程也可以使用堆维护,当然也存在线性做法。

线性做法 & 过程

同样是维护一个指针指向叶子节点中的编号最小的节点。

性质

  1. 无向完全图的不同生成树数(Cayley公式)

对于一个 \(n\) 个点的无向完全图,任意一个长度为 \(n-2\) 的 Prüfer 序列都可以构造出一个不同的具有 \(n\) 个点的树。

所以讲,会有 \(n^{n-2}\) 个不同的 Prüfer 序列构成 \(n\) 个点的树,那么无向完全图的不同生成树数也就是 \(n^{n-2}\) 个。

  1. \(n\) 个点无根树计数,且已知每个点的点度。

由于每个点在 Prüfer 序列中的出现次数为 \(d_i-1\),所以可以利用组合知识求出。

\[\binom{n-2}{b_1-1}\cdot\binom{(n-2)-(b_1-1)}{b_2-1}\cdots\binom{b_n-1}{b_n-1} \]

展开可以得到

\[\frac{(n-2)!}{\prod_{i=1}^{n}(b_i-1)} \]

posted @ 2023-06-18 11:18  Ciaxin  阅读(47)  评论(0编辑  收藏  举报