【图论】Prufer序列
凯莱定理:n个点的完全图的生成树有n^(n-2)个。
长度为n-2,值域为[1,n]的Prufer序列和一个n个节点编号分别为[1,n]的无向连通图的生成树双射。
无根树变Prufer:每次选择一个编号最小的叶子删除他,在序列中记录其父亲,直到剩下最后的两个点。(假如再做一步,则Prufer序列一定以n号点结尾,毫无意义)
用个堆也可以,直接用一个指针也可以。
下面把n-1位置也放上Prufer,为了统一处理。
for(int i = 1; i <= n; ++i) {
deg[i] = G[i].size();
vis[i] = 0;
P[i] = 0;
}
int top = 0, cur = 1;
while(top <= n - 2) {
while(deg[cur] != 1)
++cur;
int u = cur;
do {
for(int v : G[u]) {
if(vis[v] == 1)
continue;
--deg[v];
--deg[u];
P[++top] = v;
vis[u] = 1;
u = v;
break;
}
} while(u < cur && deg[u] == 1 && top <= n - 2);
}
assert(P[n - 1] == n);
每个点在Prufer中出现的次数+1就是其度数,叶子节点不存在于序列中。
Prufer变无根树:每次选择一个剩余度数为1的最小的点,把这个点删除,然后把这个点和Prufer当前的点连接,然后两端度数-1。重复n-2次后,剩下两个度数为1的点,把他们连起来。
这个也是用个堆来做最简单,或者用个指针来做也可以。
下面把n-1位置也放上Prufer,为了统一处理,但是统计度数的时候不能算上它。
P[n - 1] = n;
for(int i = 1; i <= n; ++i) {
deg[i] = 1;
vis[i] = 0;
G[i].clear();
}
for(int i = 1; i <= n - 2; ++i)
++deg[P[i]];
int top = 0, cur = 1;
while(top <= n - 2) {
while(deg[cur] != 1)
++cur;
int u = cur;
do {
int v = P[++top];
--deg[v];
--deg[u];
G[u].eb(v);
G[v].eb(u);
u = v;
} while(u < cur && deg[u] == 1 && top <= n - 2);
}