学了一种新姿势叫prufer数列,可以用来解一些关于无根树计数的问题。
prufer数列是一种无根树的编码表示,对于一棵n个节点带编号的无根树,对应唯一一串长度为n-1的prufer编码。
(1)无根树转化为prufer序列。
首先定义无根树中度数为1的节点是叶子节点。
找到编号最小的叶子并删除,序列中添加与之相连的节点编号,重复执行直到只剩下2个节点。
如下图的树对应的prufer序列就是3,5,1,3。
具体实现可以用一个set搞定,维护度数为1的节点。复杂度O(nlgn)。
-
-
-
-
- #include <iostream>
- #include <cmath>
- #include <cstdlib>
- #include <time.h>
- #include <vector>
- #include <cstdio>
- #include <cstring>
- #include <set>
- using namespace std;
- #define maxn 1111111
- #define maxm 2111111
-
- set <int> gg;
- struct node {
- int u, v, next;
- } edge[maxm];
- int n, head[maxn], cnt, degree[maxn];
- bool vis[maxn];
-
- void add_edge (int u, int v) {
- edge[cnt].u = u, edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;
- }
-
- int main () {
- cin >> n;
- memset (head, -1, sizeof head);
- memset (degree, 0, sizeof degree);
- memset (vis, 0, sizeof vis);
- gg.clear ();
- cnt = 0;
- for (int i = 1; i < n; i++) {
- int u, v;
- scanf ("%d%d", &u, &v);
- add_edge (u, v);
- add_edge (v, u);
- degree[u]++, degree[v]++;
- }
- for (int i = 1; i <= n; i++) {
- if (degree[i] == 1) {
- gg.insert (i);
- vis[i] = 1;
- }
- }
-
- set<int>::iterator it;
- int prufer[maxn], id = 0;
- for (; id <= n-3;) {
- int u = (*(it = gg.begin ()));
- gg.erase (u);
- for (int i = head[u]; i != -1; i = edge[i].next) {
- int v = edge[i].v;
- if (vis[v])
- continue;
- degree[v]--;
- prufer[++id] = v;
- if (degree[v] == 1) {
- gg.insert (v);
- vis[v] = 1;
- }
- }
- }
- for (int i = 1; i <= id; i++) {
- cout << prufer[i] << " ";
- } cout << endl;
- return 0;
- }
(2)prufer序列转化为无根树。
设点集V={1,2,3,...,n},每次取出prufer序列中最前面的元素u,在V中找到编号最小的没有在prufer序列中出现的元素v,给u,v连边然后分别删除,最后在V中剩下两个节点,给它们连边。最终得到的就是无根树。
具体实现也可以用一个set,维护prufer序列中没有出现的编号。复杂度O(nlgn)。
-
-
-
-
- #include <iostream>
- #include <cmath>
- #include <cstdlib>
- #include <time.h>
- #include <vector>
- #include <cstdio>
- #include <cstring>
- #include <set>
- using namespace std;
- #define maxn 1111111
- #define maxm 2111111
-
- int n;
- int prufer[maxn], node[maxn], cnt;
- set<int> gg;
- int vis[maxn];
- bool used[maxn];
-
- int main () {
- cin >> n;
- gg.clear ();
- memset (vis, 0, sizeof vis);
- memset (used, 0, sizeof used);
- cnt = 0;
- for (int i = 1; i <= n-2; i++) {
- cin >> prufer[i];
- vis[prufer[i]]++;
- }
- for (int i = 1; i <= n; i++) {
- if (!vis[i]) {
- gg.insert (i);
- }
- }
- set<int>::iterator it;
- for (int i = 1; i <= n-2; i++) {
- int v = (*(it = gg.begin ())), u = prufer[i];
- cout << u << "-" << v << endl;
- used[v] = 1;
- gg.erase (v);
- vis[u]--;
- if (!vis[u] && !used[u]) {
- gg.insert (u);
- }
- }
- it = gg.begin ();
- cout << *it << "-" << *(++it) << endl;
- return 0;
- }
最后有一个很重要的性质就是prufer序列中某个编号出现的次数+1就等于这个编号的节点在无根树中的度数。