• 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
    ", ans);
    106     }
    107     return 0;
    108 }

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

    本文为博主原创文章,未经博主允许不得转载。
  • 相关阅读:
    12、【常见算法】跳台阶问题
    11、【常见算法】编写CString类
    10、【常见算法】单链表逆置输出
    9、【常见算法】数组重排
    8、【常见算法】查找数组中重复元素
    7、【常见算法】数组元素查找
    6、【常见算法】整数拆分
    5、【常见算法】从长度为n的数组(元素互不相同)中任意选择m个数的所有组合。
    vi 常用命令
    Nutz框架的优点
  • 原文地址:https://www.cnblogs.com/alihenaixiao/p/4678804.html
Copyright © 2020-2023  润新知