给定一棵树,边上有权值,要统计有多少对点路径的权值和<=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 = << ;
  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 = + ;
  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] = ;
  52. int mxs = ;
  53. for (int i = head[u]; i != -; 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 != -; 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 = ;
  81. p = ;
  82. getDis(u, , dis);//得到dist数组
  83. std::sort(dist, dist + p);//每访问一个重心都要排序一次,所以是n*logn*logn
  84. int l = , r = p - ;
  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, -);//找到重心
  104. u = root;
  105. vis[u] = true;
  106. cnt[u] = ;
  107. cnt[u] += counts(u, );//统计过这个点的点对
  108. //但是会发生一个错误就是可能统计了位于同一棵子树的点
  109. for (int i = head[u]; i != -; 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 = ;
  124. memset(head, -, sizeof(head));
  125. for (int i = ;i < n;++i)
  126. {
  127. scanf("%d%d%d", &u, &v, &dis);
  128. addEdge(u, v, dis); addEdge(v, u, dis);
  129. }
  130. ans = ;
  131. size[] = ;
  132. memset(vis, , sizeof(vis));
  133. go();
  134. for (int i = ;i <= n;++i)
  135. ans += cnt[i];
  136. printf("%d\n", ans);
  137. }
  138. return ;
  139. }

树上点对统计poj1741(树的点分治)的更多相关文章

  1. 【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治

    题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F ...

  2. POJ1741——Tree(树的点分治)

    1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-11-17 1 ...

  3. POJ3659 Cell Phone Network(树上最小支配集:树型DP)

    题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...

  4. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  5. 【poj1741】Tree 树的点分治

    题目描述 Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dis ...

  6. 【bzoj1316】树上的询问 树的点分治+STL-set

    题目描述 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 输入 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行 ...

  7. HDU4871 Shortest-path tree(最短路径树 + 树的点分治)

    题目大概要先求一张边有权的图的根为1的最短路径树,要满足根到各点路径序列的字典序最小:然后求这棵最短路径树包含k个结点的最长路径的长度和个数. 首先先构造出这棵字典序最小的最短路径树..好吧,我太傻逼 ...

  8. 【bzoj4311】向量 线段树对时间分治+STL-vector维护凸包

    题目描述 你要维护一个向量集合,支持以下操作: 1.插入一个向量(x,y) 2.删除插入的第i个向量 3.查询当前集合与(x,y)点积的最大值是多少.如果当前是空集输出0 输入 第一行输入一个整数n, ...

  9. HDU4812 D Tree(树的点分治)

    题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所 ...

随机推荐

  1. ExtJs4 笔记(12) Ext.toolbar.Toolbar 工具栏、Ext.toolbar.Paging 分页栏、Ext.ux.statusbar.StatusBar 状态栏

    本篇讲解三个工具栏控件.其中Ext.toolbar.Toolbar可以用来放置一些工具类操控按钮和菜单,Ext.toolbar.Paging专门用来控制数据集的分页展示,Ext.ux.statusba ...

  2. 每天一个JavaScript实例-推断图片是否载入完毕

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  3. cocos2dx之lua项目开发中MVC框架的简单应用

    **************************************************************************** 时间:2015-03-31 作者:Sharin ...

  4. Access Violation at address 00000000.Read of address 00000000 解决办法

    是数组越标或没有初始化某个对象之类的问题,搂住细细检查一下代码, 使用指针前未做检查,而这个指针未初始化. 可能是new后没有delete,这样出现溢出的可能性比较大     检查代码或者跟踪试试 使 ...

  5. oschina图形和图像工具开源软件

    图形和图像工具开源软件 http://www.oschina.net/project/tag/181/imagetools?sort=view&lang=21&os=0

  6. IOS中的id与nil

    1 id id和void *并非完全一样.在上面的代码中,id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject) ...

  7. zoj 2822 Sum of Different Primes (01背包)

    ///给你n 求他能分解成多少个的不同的k个素数相加之和 ///01背包,素数打表 # include <stdio.h> # include <algorithm> # in ...

  8. Ubuntu14.04搭建android开发环境

    一 下载ADT 官方下载地址:http://developer.android.com/sdk/index.html(须要FQ或者改动host) 二 解压 1 使用终端将下载的文件解压当前文件夹下: ...

  9. linux根据部署jenkins

    1. Jenkins 下载 Jenkins 下载网址:http://jenkins-ci.org/ 2. Jenkins 安装 (1) 安装JDK JDK下载:http://www.oracle.co ...

  10. -bash: ./job.sh: /bin/sh^M: bad interpreter: 没有那个文件或目录

    昨天在windows下用写字板写了个shell脚本,使用winscp上传到linux上运行的时候发现运行不了,提示-bash: ./job.sh: /bin/sh^M: bad interpreter ...