传送门

良心解析

其实以前在求某段序列上的区间统计问题时就碰到过类似于这样的思想。

当时的区间统计问题思路大致是这样:

选取一个点作为中间点,从这个点的左边和右边统计出满足条件的点对。然后当前的中间点就可以删去了,接着递归统计左右两个区间的方案数。

其实这就是个分治和分类讨论的思想。

满足要求的解无非就是这两种:

1.区间包含中间点(也就是两端点在中间点的左右两边)

2.区间不包含中间点(也就是两个端点都在中间点的左边或右边)

所以正确性显而易见

淀粉质就是把这种思想应用于树上的路径统计上,选取一个点作为根,然后统计经过这个点的路径数,端点一定是在子树中的,

不过这会遇到一个问题,就是两个端点会在同一颗子树中,这就需要再统计子树中的答案再减去。接着再递归求解每一颗子树。

最后一个问题就是根节点的选取,需要选重心,防止出现链导致时间复杂度退化的情况。

其次是统计答案的方法,对于此题来说有个神奇的nlogn的方法,上面的链接中已经给出。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <algorithm>
  5. #define N 100001
  6. #define INF ~(1 << 31)
  7.  
  8. using namespace std;
  9.  
  10. int n, K, cnt, root, tot, ans;
  11. int head[N], to[N], nex[N], val[N], f[N], size[N], deep[N], d[N];
  12. bool vis[N];
  13. //f[i]表示节点i的最大子树的大小
  14. //deep[i]表示节点i到根的距离
  15. //vis[i] == 1 表示该节点删除
  16.  
  17. inline int read()
  18. {
  19. int x = 0, f = 1;
  20. char ch = getchar();
  21. for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
  22. for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
  23. return x * f;
  24. }
  25.  
  26. inline void add(int x, int y, int z)
  27. {
  28. to[cnt] = y;
  29. val[cnt] = z;
  30. nex[cnt] = head[x];
  31. head[x] = cnt++;
  32. }
  33.  
  34. //获取当前块的重心
  35. inline void getroot(int u, int fa)
  36. {
  37. int i, v;
  38. f[u] = 0;
  39. size[u] = 1;
  40. for(i = head[u]; ~i; i = nex[i])
  41. {
  42. v = to[i];
  43. if(v == fa || vis[v]) continue;
  44. getroot(v, u);
  45. size[u] += size[v];
  46. f[u] = max(f[u], size[v]);
  47. }
  48. f[u] = max(f[u], tot - size[u]);
  49. if(f[u] < f[root]) root = u;
  50. }
  51.  
  52. //获取当前块中所有的点到根的距离
  53. inline void getdeep(int u, int fa)
  54. {
  55. int i, v;
  56. deep[++deep[0]] = d[u];
  57. for(i = head[u]; ~i; i = nex[i])
  58. {
  59. v = to[i];
  60. if(v == fa || vis[v]) continue;
  61. d[v] = d[u] + val[i];
  62. getdeep(v, u);
  63. }
  64. }
  65.  
  66. //求解经过当前根的满足条件的路径的方案数
  67. inline int cal(int u, int now)
  68. {
  69. int l, r, ret = 0;
  70. d[u] = now;
  71. deep[0] = 0;
  72. getdeep(u, 0);
  73. sort(deep + 1, deep + deep[0] + 1);
  74. l = 1, r = deep[0];
  75. while(l < r)
  76. if(deep[l] + deep[r] <= K) ret += r - l++;
  77. else r--;
  78. return ret;
  79. }
  80.  
  81. //递归求解每一块
  82. inline void work(int u)
  83. {
  84. int i, v;
  85. vis[u] = 1;
  86. ans += cal(u, 0);
  87. for(i = head[u]; ~i; i = nex[i])
  88. {
  89. v = to[i];
  90. if(vis[v]) continue;
  91. //因为在求解的时候会遇到这种情况:经过当前根的满足条件的路径的两端点在同一颗子树中,这样的路径也会统计到答案中
  92. //然而并不合法,所以需要遍历每一颗子树,减去每一颗子树中满足条件的路径
  93. ans -= cal(v, val[i]);
  94. tot = size[v];
  95. root = 0;
  96. getroot(v, u);
  97. work(root);
  98. }
  99. }
  100.  
  101. int main()
  102. {
  103. int i, x, y, z;
  104. while(~scanf("%d %d", &n, &K))
  105. {
  106. if(!n && !K) break;
  107. cnt = root = ans = 0;
  108. memset(vis, 0, sizeof(vis));
  109. memset(head, -1, sizeof(head));
  110. for(i = 1; i < n; i++)
  111. {
  112. x = read();
  113. y = read();
  114. z = read();
  115. add(x, y, z);
  116. add(y, x, z);
  117. }
  118. tot = n;
  119. f[0] = INF;
  120. getroot(1, 0);
  121. work(root);
  122. printf("%d\n", ans);
  123. }
  124. return 0;
  125. }

  

[POJ1741]Tree(点分治模板)的更多相关文章

  1. POJ1741 Tree 树分治模板

    http://poj.org/problem?id=1741   题意:一棵n个点的树,每条边有距离v,求该树中距离小于等于k的点的对数.   dis[y]表示点y到根x的距离,v代表根到子树根的距离 ...

  2. [poj1741]Tree(点分治+容斥原理)

    题意:求树中点对距离<=k的无序点对个数. 解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层 ...

  3. POJ - 1741 - Tree - 点分治 模板

    POJ-1741 题意: 对于带权的一棵树,求树中距离不超过k的点的对数. 思路: 点分治的裸题. 将这棵树分成很多小的树,分治求解. #include <algorithm> #incl ...

  4. [POJ1741]Tree(点分治)

    树分治之点分治入门 所谓点分治,就是对于树针对点的分治处理 首先找出重心以保证时间复杂度 然后递归处理所有子树 对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况 路径过当前根 ...

  5. [bzoj1468][poj1741]Tree[点分治]

    可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树 ...

  6. bzoj 1468 Tree(点分治模板)

    1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1527  Solved: 818[Submit][Status][Discuss] ...

  7. [洛谷P4178] Tree (点分治模板)

    题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...

  8. POJ1741 tree (点分治模板)

    题目大意: 给一棵有 n 个顶点的树,每条边都有一个长度(小于 1001 的正整数).定义 dist(u,v)=节点 u 和 v 之间的最小距离.给定一个整数 k,对于每一对 (u,v) 顶点当且仅当 ...

  9. POJ1741 Tree + BZOJ1468 Tree 【点分治】

    POJ1741 Tree + BZOJ1468 Tree Description Give a tree with n vertices,each edge has a length(positive ...

  10. poj1741 Tree(点分治)

    题目链接:http://poj.org/problem?id=1741 题意:求树上两点之间距离小于等于k的点对的数量 思路:点分治模板题,推荐一篇讲的非常好的博客:https://blog.csdn ...

随机推荐

  1. Azure Powershell 获取可用镜像 PublisherName,Offer,Skus,Version

    #登录 $username="{登录名}" #定义一个用户账号的变量,可以输入需要登录的订阅账号名称 $password=ConvertTo-SecureString -Strin ...

  2. apache下设置域名多站点访问及禁止apache访问80端口

    apache下设置域名多站点访问 当前系统:macOS High Sierra 域名访问配置指定端口后,不同域名只能配置不同的端口 apache配置目录: sudo vim /etc/apache2/ ...

  3. SQL SERVER 2008 系列问题:无法访问,混合模式

    转载请注明:http://www.cnblogs.com/dachen408/p/7878494.html 使用本机服务器名'.'登录,使用windows模式: 1.修改登录模式为混合模式:右键服务器 ...

  4. Android的Activity之间传对象的方法

    传值代码块 //Serializeable传递对象的方法 public void SerializeMethod(){ Person mPerson = new Person(); mPerson.s ...

  5. 迅为iMX6Q/PLUS开发板烧写设备树内核 Qt 系统

    迅为iMX6Q 和 iMX6PLUS 两个硬件版本,设备树镜像的烧写方法以及镜像所在目录,镜像名称全部一致. 如果用的是 iMX6Q 版本,想要烧写设备树版本镜像,请使用 iMX6Q 设备树版本的光盘 ...

  6. POI读word doc 03 文件的两种方法

    Apache poi的hwpf模块是专门用来对word doc文件进行读写操作的.在hwpf里面我们使用HWPFDocument来表示一个word doc文档.在HWPFDocument里面有这么几个 ...

  7. MFC学习小结

    2019/1/13 视频来源 一.   MFC框架中一些重要的函数 1. InitInstance函数 应用程序类的一个虚函数,MFC应用程序的入口.初始化的作用. 2. PreCreateWindo ...

  8. Python使用三种方法实现PCA算法[转]

    主成分分析(PCA) vs 多元判别式分析(MDA) PCA和MDA都是线性变换的方法,二者关系密切.在PCA中,我们寻找数据集中最大化方差的成分,在MDA中,我们对类间最大散布的方向更感兴趣. 一句 ...

  9. GIMP暗黑诱惑,部分彩色效果制作

    在一些图形处理中经常会用到高逼格的部分彩色,其他部分黑白的效果,今天我就简单记录一下如何操作. 1.选区,先选择要突出的选区,可以用多种方法,钢笔,套绳,小剪刀等等: 2.把选择的区域稍稍调整亮一点: ...

  10. 怎么用js写一个类似于百度输入框的搜索插件

    PS:这次做的这个小插件只是在前端实现,并没有经过数据库.需要用到的的框架:1.bootstrap.css的样式 2.Vue.js 最终效果如下: JS部分: $(window).click(func ...