Fellow me on GitHub

POJ1741(SummerTrainingDay08-G 树的点分治)

Tree

Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 23380   Accepted: 7748

Description

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

Source

 
  1 //2017-08-09
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 #include <vector>
  7 
  8 using namespace std;
  9 
 10 const int N = 10010;
 11 int n, k, MAX, root, cnt, answer;
 12 
 13 //链式前向星
 14 int head[N], tot;
 15 struct Edge{
 16     int next, to, w;
 17 }edge[N<<4];
 18 
 19 void add_edge(int u, int v, int w){
 20     edge[tot].w = w;
 21     edge[tot].to = v;
 22     edge[tot].next = head[u];
 23     head[u] = tot++;
 24 }
 25 
 26 int size[N];//size[i]表示以i为根的子树的大小,包括i。
 27 int maxson[N];//maxson[i]表示以i为根的子树的最大儿子的大小。
 28 int dis[N];//dis[i]表示i到根的距离。
 29 bool vis[N];//vis[i]用来标记i点是否被删除。
 30 
 31 void init(int n){
 32     answer = 0;
 33     tot = 0;
 34     memset(vis, 0, sizeof(vis));
 35     memset(head, -1, sizeof(head));
 36 }
 37 
 38 //计算出子树的大小
 39 void dfs_size(int u, int fa){
 40     size[u] = 1;
 41     maxson[u] = 0;
 42     for(int i = head[u]; ~i; i = edge[i].next){
 43         int v = edge[i].to;
 44         if(vis[v] || v == fa)continue;
 45         dfs_size(v, u);
 46         size[u] += size[v];
 47         if(size[v] > maxson[u])
 48               maxson[u] = size[v];
 49     }
 50 }
 51 
 52 //找子树的重心。最大子树最小的点即为树的重心。
 53 void dfs_root(int r, int u, int fa){
 54     if(size[r] - size[u] > maxson[u])//size[r]-size[u]为u上面的树的尺寸
 55           maxson[u] = size[r] - size[u];
 56     if(maxson[u] < MAX){
 57         MAX = maxson[u];
 58         root = u;
 59     }
 60     for(int i = head[u]; ~i; i = edge[i].next){
 61         int v = edge[i].to;
 62         if(vis[v] || v == fa)continue;
 63         dfs_root(r, v, u);
 64     }
 65 }
 66 
 67 //计算出子树中每个点距离重心的距离
 68 void dfs_dis(int u, int d, int fa){
 69     dis[cnt++] = d;
 70     for(int i = head[u]; ~i; i = edge[i].next){
 71         int v = edge[i].to;
 72         if(vis[v] || v == fa)continue;
 73         dfs_dis(v, d+edge[i].w, u);
 74     }
 75 }
 76 
 77 //计算出以u为根的子树中距离和小于k的点对数
 78 int cal(int u, int d){
 79     int ans = 0;
 80     cnt = 0;
 81     dfs_dis(u, d, 0);
 82     sort(dis, dis+cnt);
 83     for(int i = 0, j = cnt-1; i < j; i++){
 84         while(dis[i]+dis[j] > k && i < j)//双指针
 85               j--;
 86         ans += j-i;
 87     }
 88     return ans;
 89 }
 90 
 91 //分治,找到树的重心,分为经过重心的点对和不经过重心的点对。
 92 void solve(int u){
 93     MAX = n;
 94     dfs_size(u, 0);
 95     dfs_root(u, u, 0);
 96     answer += cal(root, 0);
 97     vis[root] = 1;
 98     for(int i = head[root]; ~i; i = edge[i].next){
 99         int v = edge[i].to;
100         if(vis[v])continue;
101         answer -= cal(v, edge[i].w);
102         solve(v);
103     }
104 }
105 
106 int main()
107 {
108     //freopen("dataIn.txt", "r", stdin);
109     while(scanf("%d%d", &n, &k)!=EOF){
110         if(!n && !k)break;
111         int u, v, w;
112         init(n);
113         for(int i = 0; i < n-1; i++){
114             scanf("%d%d%d", &u, &v, &w);
115             add_edge(u, v, w);
116             add_edge(v, u, w);
117         }
118         solve(1);
119         printf("%d\n", answer);
120     }
121 
122     return 0;
123 }

 

posted @ 2017-08-09 20:22  Penn000  阅读(185)  评论(0编辑  收藏  举报