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.
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.
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})$),然后计算经过重心的链的条数,然后“删掉”重心,递归剩下的子树。
至于如何快速地处理经过重心的链的条数,主要有两种方法
- 遍历得到所有点距离重心的距离,然后用所有路径,减去在同一子树内的路径。
置于这个怎么快速计算,有个方法:
①对所有距离排个序,然后从左到右枚举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,只是我不会而已。 - 用值域线段树(动态开节点,不然我就用树状数组了),维护已经处理完的子树中的点到重心的距离对应的方案数。然后对于正在处理的子树,遍历它,算出一个点到重心的距离,然后就在值域线段树里查询(你知道该查什么)。处理完了就用子树中的数据去更新线段树。累加起来就是当前层的答案。当然写完点分你还想写带动态开节点的值域线段树吗?虽然总时间复杂度差不多,也是$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 }