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
posted @ 2014-05-03 15:59  e0e1e  阅读(243)  评论(0编辑  收藏  举报