题目链接:
题目描述:
有一颗树,每个边都有权值,w问有多少点对(u,v),满足从u直接或者间接到v的代价不超过k?
解题思路:
树上点的分治模板题。
分治要注意递归的深度,每次向下分治是要找到树的重心才能保证复杂度为O(logn),否则复杂度能上升到O(n)。
所以用分治搞的童雪们,如果TLE了的话,就回去看看自己每次分治求得重心对不对咯。
每次分治对于路径就分为两种:
1:路径经过根节点。
2:路径经过根节点,但是在根节点的一个子树里。
分治时候只需要求出第一种路径减去第二种路径累加起来就好。
这个题目Tle的好苦啊,原来一直是树的重心没找对,Tle好长时间,终于对了,好感动.>_<.。(PS:Poj1987,树的分治做一送一大派送)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 10010; 8 struct node 9 { 10 int to, w, next; 11 }edge[maxn*2]; 12 int head[maxn], vis[maxn], size[maxn], mu[maxn]; 13 int tot, n, k, ans, root, mini, d, dis[maxn]; 14 15 void init () 16 { 17 tot = ans = 0; 18 size[1] = n;//i为根节点的子树节点数目 19 memset (head, -1, sizeof(head)); 20 memset (vis, 0, sizeof(vis)); 21 } 22 void Add (int from, int to, int val) 23 { 24 edge[tot].to = to; 25 edge[tot].w = val; 26 edge[tot].next = head[from]; 27 head[from] = tot ++; 28 } 29 void dfsroot (int u, int father) 30 {//寻找u所在子树的重心 31 size[u] = 1; 32 mu[u] = 0; 33 for (int i=head[u]; i!=-1; i=edge[i].next) 34 { 35 int v = edge[i].to; 36 if (!vis[v] && v!=father) 37 { 38 dfsroot (v, u); 39 size[u] += size[v]; 40 mu[u] = max(mu[u], size[v]); 41 } 42 } 43 mu[u] = max (mu[u], n-size[u]); 44 if (mini > mu[u]) 45 { 46 mini = mu[u]; 47 root = u; 48 } 49 } 50 void dfsdist (int u, int father, int w) 51 {//统计路径长度 52 dis[d ++] = w; 53 for (int i=head[u]; i!=-1; i=edge[i].next) 54 { 55 int v = edge[i].to; 56 if (!vis[v] && v!=father) 57 dfsdist (v, u, w+edge[i].w); 58 } 59 } 60 int calc (int u, int w) 61 {//计算以u为根节点的子树中合法路径数目 62 int res = 0; 63 d = 0; 64 dfsdist (u, 0, w); 65 sort (dis, dis+d); 66 int i=0, j=d-1; 67 while (i < j) 68 {//相对搜索,复杂度O(n) 69 while (dis[i] + dis[j] > k && i<j) 70 j --; 71 res += j - i; 72 i ++; 73 } 74 return res; 75 } 76 void dfs (int u) 77 {//分治 78 mini = n = size[u];//每次n一定要更新为当前子树的节点总数 79 dfsroot (u, 0); 80 ans += calc (root, 0); 81 vis[root] = 1; 82 for (int i=head[root]; i!=-1; i=edge[i].next) 83 { 84 int v = edge[i].to; 85 if (!vis[v]) 86 { 87 ans -= calc (v, edge[i].w); 88 dfs (v); 89 } 90 } 91 } 92 int main () 93 { 94 while (scanf ("%d %d", &n, &k), n+k) 95 { 96 init (); 97 for (int i=1; i<n; i++) 98 { 99 int u, v, w; 100 scanf ("%d %d %d", &u, &v, &w); 101 Add (u, v, w); 102 Add (v, u, w); 103 } 104 dfs (1); 105 printf ("%d ", ans); 106 } 107 return 0; 108 }
每次写递归都感觉很舒服,竟然有些喜欢树上的算法了