frufer 序列(no look)

学了一种新姿势叫prufer数列,可以用来解一些关于无根树计数的问题。

prufer数列是一种无根树的编码表示,对于一棵n个节点带编号的无根树,对应唯一一串长度为n-1的prufer编码。

(1)无根树转化为prufer序列。

首先定义无根树中度数为1的节点是叶子节点。

找到编号最小的叶子并删除,序列中添加与之相连的节点编号,重复执行直到只剩下2个节点。

如下图的树对应的prufer序列就是3,5,1,3。


具体实现可以用一个set搞定,维护度数为1的节点。复杂度O(nlgn)。

[cpp]  view plain  copy
  1. /********************** 
  2. 给一棵无根树进行prufer编码 
  3. **********************/  
  4.   
  5. #include <iostream>  
  6. #include <cmath>  
  7. #include <cstdlib>  
  8. #include <time.h>  
  9. #include <vector>  
  10. #include <cstdio>  
  11. #include <cstring>  
  12. #include <set>  
  13. using namespace std;  
  14. #define maxn 1111111  
  15. #define maxm 2111111  
  16.   
  17. set <int> gg;  
  18. struct node {  
  19.     int u, v, next;  
  20. } edge[maxm];  
  21. int n, head[maxn], cnt, degree[maxn];  
  22. bool vis[maxn];  
  23.   
  24. void add_edge (int u, int v) {  
  25.     edge[cnt].u = u, edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;  
  26. }  
  27.   
  28. int main () {  
  29.     cin >> n;  
  30.     memset (head, -1, sizeof head);  
  31.     memset (degree, 0, sizeof degree);  
  32.     memset (vis, 0, sizeof vis);  
  33.     gg.clear ();  
  34.     cnt = 0;  
  35.     for (int i = 1; i < n; i++) {  
  36.         int u, v;  
  37.         scanf ("%d%d", &u, &v);  
  38.         add_edge (u, v);  
  39.         add_edge (v, u);  
  40.         degree[u]++, degree[v]++;  
  41.     }  
  42.     for (int i = 1; i <= n; i++) {  
  43.         if (degree[i] == 1) {  
  44.             gg.insert (i);  
  45.             vis[i] = 1;  
  46.         }  
  47.     }  
  48.   
  49.     set<int>::iterator it;  
  50.     int prufer[maxn], id = 0;  
  51.     for (; id <= n-3;) {  
  52.         int u = (*(it = gg.begin ()));  
  53.         gg.erase (u);  
  54.         for (int i = head[u]; i != -1; i = edge[i].next) {  
  55.             int v = edge[i].v;  
  56.             if (vis[v])  
  57.                 continue;  
  58.             degree[v]--;  
  59.             prufer[++id] = v;  
  60.             if (degree[v] == 1) {  
  61.                 gg.insert (v);  
  62.                 vis[v] = 1;  
  63.             }  
  64.         }  
  65.     }  
  66.     for (int i = 1; i <= id; i++) {  
  67.         cout << prufer[i] << " ";  
  68.     } cout << endl;  
  69.     return 0;  
  70. }  

(2)prufer序列转化为无根树。

设点集V={1,2,3,...,n},每次取出prufer序列中最前面的元素u,在V中找到编号最小的没有在prufer序列中出现的元素v,给u,v连边然后分别删除,最后在V中剩下两个节点,给它们连边。最终得到的就是无根树。

具体实现也可以用一个set,维护prufer序列中没有出现的编号。复杂度O(nlgn)。

[cpp]  view plain  copy
  1. /********************** 
  2. prufer序列解码 
  3. **********************/  
  4.   
  5. #include <iostream>  
  6. #include <cmath>  
  7. #include <cstdlib>  
  8. #include <time.h>  
  9. #include <vector>  
  10. #include <cstdio>  
  11. #include <cstring>  
  12. #include <set>  
  13. using namespace std;  
  14. #define maxn 1111111  
  15. #define maxm 2111111  
  16.   
  17. int n;  
  18. int prufer[maxn], node[maxn], cnt;  
  19. set<int> gg; //在prufer序列里没有出现的点  
  20. int vis[maxn]; //这个点是不是在prufer序列里面  
  21. bool used[maxn]; //这个点有没有使用过  
  22.   
  23. int main () {  
  24.     cin >> n;  
  25.     gg.clear ();  
  26.     memset (vis, 0, sizeof vis);  
  27.     memset (used, 0, sizeof used);  
  28.     cnt = 0;  
  29.     for (int i = 1; i <= n-2; i++) {  
  30.         cin >> prufer[i];  
  31.         vis[prufer[i]]++;  
  32.     }  
  33.     for (int i = 1; i <= n; i++) {  
  34.         if (!vis[i]) {  
  35.             gg.insert (i);  
  36.         }  
  37.     }  
  38.     set<int>::iterator it;  
  39.     for (int i = 1; i <= n-2; i++) {  
  40.         int v = (*(it = gg.begin ())), u = prufer[i];  
  41.         cout << u << "-" << v << endl;  
  42.         used[v] = 1;  
  43.         gg.erase (v);  
  44.         vis[u]--;  
  45.         if (!vis[u] && !used[u]) {  
  46.             gg.insert (u);  
  47.         }  
  48.     }  
  49.     it = gg.begin ();  
  50.     cout << *it << "-" << *(++it) << endl;  
  51.     return 0;  
  52. }  

最后有一个很重要的性质就是prufer序列中某个编号出现的次数+1就等于这个编号的节点在无根树中的度数。

2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值