📚【模板】prufer序列

  1. 什么是prufer序列

就是一个长度为\(n-2\)的序列,与\(n\)个点的有编号无根树形成双射。

prufer序列中每个点出现其对应无根树度数减一次。

  1. 由树构造prufer序列?

每次选择一个编号最小的叶节点,删掉它并在序列中记录下它连接的那个节点,重复\(n-2\)次后只剩两个节点,算法结束。

用堆的话是\(n\log n\),但用一个指针可优化到\(n\)

指针指向度数为\(1\)的编号最小的节点并删除。若产生新的叶节点且其编号比指针小,则立刻删除,并不断重复该判断至未产生新的叶节点或者新的叶节点编号比指针大。

  1. prufer序列构造树?

每次选择一个编号最小的度数为\(1\)的节点与当前枚举到的prufer序列的点连接并将其度数减一。重复\(n-2\)次后只剩两个节点,把它们连接起来,算法结束。

用堆的话是\(n\log n\),但用一个指针可优化到\(n\)

指针指向编号最小的度数为\(1\)的节点,每次将它与当前枚举到的prufer序列的点连接并将其度数减一,若产生了新的度数为\(1\)的节点且编号比指针指向的更小,则直接继续将它与下一个prufer序列的点连接。

  1. 例题

\(\text{luogu P6086 【模板】Prufer 序列}\)

#include <stdio.h>
const int N = 5242880;
int n, opt;
int parent[N], degree[N];
int prufer[N];
long long val_ans;
signed main() {
	scanf("%d %d",&n,&opt);
	if(opt == 1) {
		for(int i = 1;i < n;++i) {
			scanf("%d",&parent[i]);
			++degree[parent[i]];
		}
		for(int i = 1, j = 1;i <= n-2;++i, ++j) {
			while(degree[j]) 
				++j;
			prufer[i] = parent[j];
			while(i <= n-2&&!--degree[prufer[i]]&&prufer[i] < j) {
				prufer[i+1] = parent[prufer[i]];
				++i;
			}
		}
		for(int i = 1;i <= n-2;++i) 
			val_ans = val_ans^((long long)i*prufer[i]);
		printf("%lld\n",val_ans);
	} else {
		for(int i = 1;i <= n-2;++i) {
			scanf("%d",&prufer[i]);
			++degree[prufer[i]];
		}
		prufer[n-1] = n;
		for(int i = 1, j = 1;i < n;++i, ++j) {
			while(degree[j]) 
				++j;
			parent[j] = prufer[i];
			while(i < n&&!--degree[prufer[i]]&&prufer[i] < j) {
				parent[prufer[i]] = prufer[i+1];
				++i;
			}
		}
		for(int i = 1;i < n;++i) 
			val_ans = val_ans^((long long)i*parent[i]);
		printf("%lld\n",val_ans);
	}
}
  1. 真正有用的东西
  • Cayley定理\(n\)个点的完全图,有\(n^{n-2}\)棵生成树。

  • \(n\)个点,每个点的度数为\(degree_i\)的无根树有\(\large\frac{(n-2)!}{\prod\limits_{i = 1}^{n}(degree_i-1)!}\)种。

  • \(n\)个点,\(k\)个连通块,每个连通块\(s_i\)个点,添加\(k-1\)条边,使图连通,方案有\(n^{k-2}\times\prod\limits_{i = 1}^{k}s_i\)

posted @ 2022-07-31 12:07  bikuhiku  阅读(25)  评论(0编辑  收藏  举报