poj 1741 Tree - 树分治

  Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v. 
  Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. 
Write a program that will count how many pairs which are valid for a given tree. 

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l. 
The last test case is followed by two zeros. 

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8

  题目大意讲的是给定一棵带边权的树,求有多少对点,之间的距离不超过给定的k。

  树分治的经典例题。我用的点分,每次用遍历去找重心(降低时间复杂度,防止退化成链,导致时间复杂度变为$O(n^{2})$),然后计算经过重心的链的条数,然后“删掉”重心,递归剩下的子树。

  至于如何快速地处理经过重心的链的条数,主要有两种方法

  1. 遍历得到所有点距离重心的距离,然后用所有路径,减去在同一子树内的路径。
    置于这个怎么快速计算,有个方法:
    ①对所有距离排个序,然后从左到右枚举i,因为距离是从小到大的,它们的总和又不超过k,那么最大的deps[j]和deps[i]的和不超过k的j一定是递减的,所以可以实现O(n)来计算。
    ②如果j > i那么就给数量加上(j - i)(i不能自己到自己,所以有j - i个点到它的距离满足条件),否则就可以break了。
    用这个方法先处理重心的子树,然后再处理当前子树,就可以得到当前这一层的答案。总时间复杂度$O\left(n\log^{2} n\right)$。当然也可以做到nlogn,只是我不会而已。
  2. 用值域线段树(动态开节点,不然我就用树状数组了),维护已经处理完的子树中的点到重心的距离对应的方案数。然后对于正在处理的子树,遍历它,算出一个点到重心的距离,然后就在值域线段树里查询(你知道该查什么)。处理完了就用子树中的数据去更新线段树。累加起来就是当前层的答案。当然写完点分你还想写带动态开节点的值域线段树吗?虽然总时间复杂度差不多,也是$O\left(n\log^{2} n\right)$(算错了别怪我)

  求树的重心,不懂就看一下这里:指定一个节点作为“根”,一次dfs得到每个节点及其子树的大小。然后for,它上面的节点数就用总结点数减去size[node],按照定义求重心就行了。

Code(极其不简洁的代码)

  1 /**
  2  * poj.org
  3  * Problem#1741
  4  * Accepted
  5  * Time:172ms
  6  * Memory:1476k
  7  */
  8 #include<iostream>
  9 #include<fstream>
 10 #include<sstream>
 11 #include<cstdio>
 12 #include<cstdlib>
 13 #include<cstring>
 14 #include<ctime>
 15 #include<cctype>
 16 #include<cmath>
 17 #include<algorithm>
 18 #include<stack>
 19 #include<queue>
 20 #include<set>
 21 #include<map>
 22 #include<vector>
 23 #ifndef WIN32
 24 #define AUTO "%lld"
 25 #else
 26 #define AUTO "%I64d"
 27 #endif
 28 using namespace std;
 29 typedef bool boolean;
 30 #define inf 0xfffffff
 31 #define smin(a, b)    (a) = min((a), (b))
 32 #define smax(a, b)    (a) = max((a), (b))
 33 template<typename T>
 34 inline void readInteger(T& u){
 35     char x;
 36     int aFlag = 1;
 37     while(!isdigit((x = getchar())) && x != '-' && x != -1);
 38     if(x == -1)    return;
 39     if(x == '-'){
 40         x = getchar();
 41         aFlag = -1;
 42     }
 43     for(u = x - '0'; isdigit((x = getchar())); u = (u << 3) + (u << 1) + x - '0');
 44     ungetc(x, stdin);
 45     u *= aFlag;
 46 }
 47 
 48 ///map template starts
 49 typedef class Edge{
 50     public:
 51         int end;
 52         int next;
 53         int w;
 54         Edge(const int end = 0, const int next = 0, const int w = 0):end(end), next(next), w(w){}
 55 }Edge;
 56 typedef class MapManager{
 57     public:
 58         int ce;
 59         int *h;
 60         Edge *edge;
 61         MapManager(){}
 62         MapManager(int points, int limit):ce(0){
 63             h = new int[(const int)(points + 1)];
 64             edge = new Edge[(const int)(limit + 1)];
 65             memset(h, 0, sizeof(int) * (points + 1));
 66         }
 67         inline void addEdge(int from, int end, int w){
 68             edge[++ce] = Edge(end, h[from], w);
 69             h[from] = ce;
 70         }
 71         inline void addDoubleEdge(int from, int end, int w){
 72             addEdge(from, end, w);
 73             addEdge(end, from, w);
 74         }
 75         Edge& operator [](int pos) {
 76             return edge[pos];
 77         }
 78         inline void clear() {
 79             delete[] h;
 80             delete[] edge;
 81             ce = 0;
 82         }
 83 }MapManager;
 84 #define m_begin(g, i) (g).h[(i)]
 85 ///map template ends
 86 
 87 int n, k;
 88 MapManager g;
 89 int* size;            //大小 
 90 boolean* visited;    //是否做过重心 
 91 
 92 inline boolean init() {
 93     readInteger(n);
 94     readInteger(k);
 95     if(n == 0 && k == 0)    return false;
 96     g = MapManager(n, 2 * n);
 97     size = new int[(const int)(n + 1)];
 98     visited = new boolean[(const int)(n + 1)];
 99     memset(visited, false, sizeof(boolean) * (n + 1));
100     for(int i = 1, a, b, c; i < n; i++) {
101         readInteger(a);
102         readInteger(b);
103         readInteger(c);
104         g.addDoubleEdge(a, b, c);
105     }
106     return true;
107 }
108 
109 void getSize(int node, int fa){            //计算大小 
110     size[node] = 1;
111     for(int i = m_begin(g, node); i != 0; i = g[i].next) {
112         int &e = g[i].end;
113         if(e == fa || visited[e])    continue;
114         getSize(e, node);
115         size[node] += size[e];
116     }
117 }
118 
119 void getG(int node, int fa, int& G, int& minv, int all) { //得到重心 
120     int c = all - size[node];
121     for(int i = m_begin(g, node); i != 0; i = g[i].next) {
122         int& e = g[i].end;
123         if(e == fa || visited[e])    continue;
124         smax(c, size[e]);
125         getG(e, node, G, minv, all);
126     }
127     if(c < minv)    G = node, minv = c;
128 }
129 
130 int top;
131 int* dep;            //到当前重心的距离 
132 void getDeps(int node, int fa, int d) {        //求距离 
133     dep[++top] = d;
134     for(int i = m_begin(g, node); i != 0; i = g[i].next) {
135         int &e = g[i].end;
136         if(e == fa || visited[e])    continue;
137         getDeps(e, node, d + g[i].w);
138     }
139 }
140 
141 int calcCount(int* deps, int from, int end) {    //计算数量 
142     if(from >= end)    return 0;
143     int ret = 0;
144     sort(deps + from, deps + end + 1);
145     int r = end;
146     for(int i = from; i <= end; i++) {
147         while(deps[i] + deps[r] > k && r > i)    r--;
148         if(r == i)    return ret;
149         ret += r - i;
150     }
151     return ret;
152 }
153 
154 long long res;
155 void work(int node) {                //点分治主过程 
156     int G, minv = inf;
157     getSize(node, 0);
158     if(size[node] == 1)    return;
159     getG(node, 0, G, minv, size[node]);
160     top = 1;
161     int b = 1;
162     dep[1] = 0;
163     for(int i = m_begin(g, G); i != 0; i = g[i].next) {
164         int& e = g[i].end;
165         if(visited[e])    continue;
166         getDeps(e, G, g[i].w);
167         res -= calcCount(dep, b + 1, top);
168         b = top; 
169     }
170     visited[G] = true;
171     res += calcCount(dep, 1, top);
172     for(int i = m_begin(g, G); i != 0; i = g[i].next) {
173         int& e = g[i].end;
174         if(visited[e])    continue;
175         work(e);
176     }
177 }
178 
179 inline void solve() {
180     res = 0;
181     dep = new int[(const int)(n + 1)];
182     work(1);
183     printf(AUTO"\n", res);
184     delete[] dep;
185     delete[] size;
186     delete[] visited;
187     g.clear();
188 }
189 
190 int main() {
191     while(init()) {
192         solve();
193     }
194     return 0;
195 }
posted @ 2017-03-20 22:48  阿波罗2003  阅读(194)  评论(0编辑  收藏  举报