Poj 1741 Tree (树的分治)

题目链接:

  Poj 1741 Tree

题目描述:

  有一颗树,每个边都有权值,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\n", ans);
106     }
107     return 0;
108 }

每次写递归都感觉很舒服,竟然有些喜欢树上的算法了

posted @ 2015-07-26 21:49  罗茜  阅读(477)  评论(0编辑  收藏  举报