• 树上点对统计poj1741(树的点分治)


    给定一棵树,边上有权值,要统计有多少对点路径的权值和<=k

    分治算法在树的路径中的应用 这个论文里面有分析。

    任意两点的路径,要么过根结点,要么在子树中。如果在子树中,那么只要递归处理就行了。

    所以只要统计过根结点的对数。

    所以只要算出任意结点到根结点的距离,用dist数组存下来,然后排序dist数组,就可以在O(n)的时间内算出有多少对点满足条件,具体见counts函数

    但是counter函数计算时,会把路径都在子树中的情况也给加进来,所以需要再对相应的子树进行counts函数,然后减去。

    然后在递归计算路径经过孩子结点的情况。

    但是这样子的算法是会退化的,因为树可能是一条链。

    所以就需要用到重心了。 因为重心具有的一个性质是删掉重心后,可以使得剩下的分支中,最大的最小。

    所以在算出所有经过u的点对之后,递归计算v这个子树时,相当于计算一棵全新的树,所以只要找到这棵树的重心,继续计算就行了。

    因为结点数可以每次都减少为原来的一般,所以层数只有logn层, 每层统计时,排序要nlogn,所以总的时间复杂度是n*logn*logn

      1 #pragma warning(disable:4996)
      2 #pragma comment(linker, "/STACK:1024000000,1024000000")
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <time.h>
      6 #include <math.h>
      7 #include <map>
      8 #include <set>
      9 #include <queue>
     10 #include <stack>
     11 #include <vector>
     12 #include <bitset>
     13 #include <algorithm>
     14 #include <iostream>
     15 #include <string>
     16 #include <functional>
     17 const int INF = 1 << 30;
     18 typedef __int64 LL;
     19 /*
     20 dist(u,v)为u,v两点路径上所有边的权和
     21 如果dist(u,v)<=k,那么称(a,b)为合法点对
     22 求合法点对的个数
     23 N<=10000, k<=10^9
     24 
     25 depth(i) + depth(j) <=k 且belong(i)!=belong(j)
     26 
     27 我的问题是? 分治的时候,如何将树两边的点合起来???
     28 */
     29 const int N = 20000 + 10;
     30 struct Node
     31 {
     32     int to, next, dis;
     33 }g[N];
     34 int head[N], e;
     35 int n, k, ans;
     36 int size[N];
     37 int mins, total, root;
     38 int dist[N], p;
     39 int vis[N];
     40 int cnt[N];
     41 void addEdge(int u, int v, int dis)
     42 {
     43     g[e].to = v;
     44     g[e].dis = dis;
     45     g[e].next = head[u];
     46     head[u] = e++;
     47 }
     48 
     49 void dfsRoot(int u, int f)
     50 {
     51     size[u] = 1;
     52     int mxs = 0;
     53     for (int i = head[u]; i != -1; i = g[i].next)
     54     {
     55         int v = g[i].to;
     56         if (v == f || vis[v]) continue;
     57         dfsRoot(v, u);
     58         size[u] += size[v];
     59         mxs = std::max(mxs, size[v]);
     60     }
     61     //下面是找重心的代码
     62     if(mxs < total - size[u])
     63         mxs = total - size[u];
     64     if (mxs < mins)
     65         mins = mxs, root = u;
     66 }
     67 
     68 void getDis(int u, int f, int dis)
     69 {
     70     dist[p++] = dis;//遍历没有访问过的点,将到根结点的距离存下来
     71     for (int i = head[u]; i != -1; i = g[i].next)
     72     {
     73         int v = g[i].to;
     74         if (v == f || vis[v]) continue;
     75         getDis(v, u, dis + g[i].dis);
     76     }
     77 }
     78 int counts(int u, int dis)
     79 {
     80     int ret = 0;
     81     p = 0;
     82     getDis(u, 0, dis);//得到dist数组
     83     std::sort(dist, dist + p);//每访问一个重心都要排序一次,所以是n*logn*logn
     84     int l = 0, r = p - 1;
     85     while (l < r)
     86     {
     87         //如果dist[l]+dist[r]<=k ,那么dist[l]+任意的点都是可行的
     88         if (dist[l] + dist[r] <= k)
     89         {
     90             ret += (r - l);
     91             l++;//判断下一个l
     92         }
     93         else//如果这个r不行,那么这个r与谁结合都是不行的
     94             r--;
     95     }
     96     return ret;
     97 }
     98 
     99 void go(int u)
    100 {
    101     //total存的是子树所有结点
    102     mins = INF, total = size[u] ? size[u] : n;
    103     dfsRoot(u, -1);//找到重心
    104     u = root;
    105     vis[u] = true;
    106     cnt[u] = 0;
    107     cnt[u] += counts(u, 0);//统计过这个点的点对
    108     //但是会发生一个错误就是可能统计了位于同一棵子树的点
    109     for (int i = head[u]; i != -1; i = g[i].next)
    110     {
    111         int v = g[i].to;
    112         if (vis[v]) continue;
    113         cnt[u] -= counts(v, g[i].dis);//所有要减去位于同一棵子树的点
    114         go(v);
    115     }
    116 }
    117 int main()
    118 {
    119     
    120     while(scanf("%d%d", &n, &k),n+k)
    121     {
    122         int u, v, dis;
    123         e = 0;
    124         memset(head, -1, sizeof(head));
    125         for (int i = 1;i < n;++i)
    126         {
    127             scanf("%d%d%d", &u, &v, &dis);
    128             addEdge(u, v, dis); addEdge(v, u, dis);
    129         }
    130         ans = 0;
    131         size[1] = 0;
    132         memset(vis, 0, sizeof(vis));
    133         go(1);
    134         for (int i = 1;i <= n;++i)
    135             ans += cnt[i];
    136         printf("%d
    ", ans);
    137 }
    138     return 0;
    139 }
    View Code
  • 相关阅读:
    人月神话阅读笔记01
    Map Reduce数据清洗及Hive数据库操作
    Hadoop实验六——MapReduce的操作
    假期第九周学习记录
    假期第八周学习记录
    假期第七周学习记录
    hadoop不在sudoers文件中。此事将被报告。 解决方法
    假期第六周学习记录
    2021寒假(22)
    2021寒假(21)
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4766463.html
Copyright © 2020-2023  润新知