POJ 1741 - Tree
LTC男人八题系列,测试数据之强真不是盖的。
题目大意是在树上找出所有满足长度<=K的简单路径数。首先最朴素的就是就是针对每一个节点dfs一下因为没回路一次是O(N)级别合起来就是O(N2)算法了,铁定超时的节奏。
看看有没有更快的算法。首先如果选出一个节点,可以知道,要么路径经过这个结点,要么这个路径不经过。
如果路径经过这个节点,针对简单路径,那么这个路径的两端肯定分别位于这个节点下的两个不同的子树中。
这时很明确的分治思路就出来了,如果不经过这个节点,那我让它经过这个节点的某个子树的根节点好吧,这就递归了(如图1,在这些圆框框的范围里计算出经过各自粉框框的所有路径,统计下是不是就全了?)。
但现在还有一个问题,我们怎么才能筛选出不在同一子树情况呢?可以正面做,那就是一个集合的排列组合问题了,好麻烦,有没有更好的办法?
我们干脆求出经过某节点的所有满足<=K的路径数,不管它是不是简单的路径,然后再找那些在一个子树里的经过根节点的复杂路径,一个子树一个子树的减是不是就ok了?
如图,看黄圈,其实我们一般认为的路径是红色的简单路径,但通过dfs计算的绿色路径数也有如图且满足长度<=K的,我们要dfs这些子树结点来删除这些绿色路径。让它经过子树的根节点来处理。另外找重心也比较重要,因为分治后的问题基本是独立的,而找到了重心可以让问题规模减少的最快(想想理想情况下的快速排序)。总算法时间复杂度是O(N(logN)2)
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 5 6 #define FOR(p,i,s,t) for(__typeof(p) i=s; i<t; ++i) 7 #define REP(t,i,n) FOR(t,i,0,n) 8 9 #define ECH(it, A) for (__typeof(A.begin()) it=A.begin(); it != A.end(); ++it) 10 #define RST(x,y) memset(x, y, sizeof(x)) 11 #define RST0(x) RST(x,0) 12 13 typedef int Vt, Lt; 14 const __typeof(Vt) MAXV = 10005; 15 16 #define MAXE ((MAXV<<1) - 2) 17 18 Vt Vefw[MAXE], Veh[MAXV], eptr = 0; 19 struct Vedge { 20 Vt t; 21 Lt l; 22 Vedge() {} 23 Vedge(Vt _t): t(_t), l(1) {} 24 Vedge(Vt _t, Lt _l): t(_t), l(_l) {} 25 void attach(Vt s) { 26 extern Vedge Vs[]; 27 Vs[eptr].t = this->t, Vs[eptr].l = this->l; 28 Vefw[eptr] = Veh[s]; Veh[s] = ++eptr; 29 } 30 }; 31 #define addedge(s,t,l) ({Vedge e(t,l); e.attach(s);}) 32 Vedge Vs[MAXE]; 33 Vt gcoref_tot; 34 char gc_8[MAXV]; 35 Vt gc_maxk[MAXV], gc_sumn[MAXV]; 36 37 int gc_root; 38 39 void gcoref(Vt i) 40 { 41 char _gc8; 42 if (!(_gc8 = gc_8[i])) gc_8[i] = -1; // 遍历去环 43 Vt sumn = 1, maxk = 0; 44 for(Vt e = Veh[i]; e; e = Vefw[e]) { 45 Vt t = Vs[--e].t; 46 if (!gc_8[t]) { 47 gcoref(t); 48 sumn += gc_sumn[t]; 49 if (maxk < gc_sumn[t]) 50 maxk = gc_sumn[t]; 51 } 52 } 53 gc_8[i] = _gc8; // gc_8还有其他用途 54 if (gcoref_tot - sumn > maxk) maxk = gcoref_tot - sumn; 55 gc_sumn[i] = sumn, gc_maxk[i] = maxk; 56 if (gc_maxk[gc_root] > maxk) gc_root = i; 57 } 58 59 inline Vt gcore(Vt root) 60 { 61 gc_maxk[gc_root = root] = gcoref_tot; 62 gcoref(root); 63 return gc_root; 64 } 65 66 int K; 67 #define dist gc_sumn 68 Vt sorted_dist[MAXV], sorted_tot; 69 70 void upddis(Vt s) 71 { 72 char _gc8; 73 if (!(_gc8 = gc_8[s])) gc_8[s] = -1; // 遍历去环 74 for(Vt e = Veh[s]; e; e = Vefw[e]) { 75 Vt t = Vs[--e].t; 76 if (!gc_8[t]) { 77 if ((dist[t] = dist[s] + Vs[e].l) <= K) { 78 sorted_dist[sorted_tot++] = dist[t]; 79 upddis(t); 80 } 81 } 82 } 83 gc_8[s] = _gc8; 84 } 85 86 #include <algorithm> 87 88 int calcpairs(Lt walked, Vt s) 89 { 90 sorted_dist[0] = dist[s] = walked, sorted_tot = 1; 91 upddis(s); 92 sort(sorted_dist, sorted_dist + sorted_tot); 93 94 int pairs = 0; 95 Vt i, j; 96 97 i = 0, j = sorted_tot - 1; 98 while(i < j) 99 if (sorted_dist[i] + sorted_dist[j] <= K) 100 pairs += j - i++; 101 else --j; 102 return pairs; 103 } 104 105 Vt subtree_ntot[MAXV]; 106 107 int solve(Vt s) 108 { 109 Vt root = gcore(s); 110 for(Vt e = Veh[root]; e; e = Vefw[e]) { // 保护子树的结点数 111 Vt t = Vs[--e].t; 112 subtree_ntot[t] = gc_sumn[t]; 113 } 114 int pairs = calcpairs(0, root); 115 gc_8[root] = 2; 116 for(Vt e = Veh[root]; e; e = Vefw[e]) { 117 Vt t = Vs[--e].t; 118 if (!gc_8[t]) { 119 gcoref_tot = subtree_ntot[t]; 120 pairs -= calcpairs(Vs[e].l, t); 121 pairs += solve(t); 122 } 123 } 124 return pairs; 125 } 126 127 Vt N; 128 129 int main(void) 130 { 131 // freopen("poj1741.txt", "r", stdin); 132 while(scanf("%d%d", &N,&K), N && K) { 133 Vt root = 0; 134 REP(int, i, N-1) { 135 Lt l; 136 Vt s,t; 137 scanf("%d%d%d", &s, &t, &l); --s, --t; 138 addedge(s,t,l), addedge(t,s,l); 139 if (root == t) root = s; 140 } 141 gcoref_tot = N; 142 printf("%d\n", solve(root)); 143 RST0(gc_8), RST0(Veh), eptr = 0; 144 } 145 return 0; 146 }
1741 | Accepted | 1124K | 219MS | G++ | 2968B | 2014-05-03 15:26:17 |
This article is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。