(题面来自luogu)

题目描述

给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K

输入格式

N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k

输出格式

一行,有多少对点之间的距离小于等于k

  原本是点分治的模版题,从昨晚调到今晚……这里记录下点分治实现时需要注意的几个细节。

1、分治过程中递归子树大小的确定

  以下是点分治过程的核心函数,其中cur表示以u为根进行分治的树的大小。

  1. void Divide(int u) {
  2. vis[u] = true;
  3. ans += Solve(u, 0);
  4. int tcur = cur;
  5. for (int i = head[u]; i; i = edge[i].nxt) {
  6. int v = edge[i].to;
  7. if (vis[v]) continue;
  8. ans -= Solve(v, edge[i].w);
  9. Mn = inf;
  10. //cur = size[v];
  11. cur = size[u] > size[v] ? size[v] : tcur - size[u];
  12. Find_rt(v, u);
  13. Divide(root);
  14. }
  15. }

  重点在第10、11行递归子树大小确定的两种写法,其中第11行未注释的版本是正确的。考虑到我们每次在当前树中选重心为根进行分治,那么u并不一定是该树在搜索树意义下的根节点。也就是说,u的子节点v有可能是u在搜索树上的父亲,因此在确定递归子树大小时加入一个特判。因为cur的值会因为遍历先前的v而改变,我们在第4行用一个新变量记录当前树的大小。这个就是调了一天的锅的出处

  (不过据说不加这个判断复杂度也不会劣化……貌似还有人证明了,不过保证正确性显然是好的)

2、关于点分治两种写法的优劣

  点分治不同写法的讲解请见我的博客:https://www.cnblogs.com/TY02/p/11203163.html

  之前认为用容斥算两遍的做法常数过大,比较起来把子树分开互相统计更好。实际上第二种做法有它的局限性:例如在这个题中,暴力枚举每条路径会T飞,我们只能把u子树中所有的节点深度都统计一遍,排序后利用单调性用双指针统计答案。这就暴露了分子树统计的劣势,它只可以把子树中两点不重不漏地两两枚举、组合路径信息,无法在其中嵌套别的操作。容斥的优点在于它把所有的节点信息一次性统计出来,适合类似本题利用数据单调性排序来统计的情形。这个题也不排序也可以用权值树状数组来做,复杂度相同,常数因为要清空数组会大一些。

完整代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #define BUG puts("$$$")
  6. #define rint register int
  7. #define maxn 40010
  8. typedef long long ll;
  9. using namespace std;
  10. const int inf = (int)1e9;
  11. template <typename T>
  12. void read(T &x) {
  13. x = 0;
  14. char ch = getchar();
  15. //  int f = 1;
  16. while (!isdigit(ch)) {
  17. //      if (ch == '-') f = -1;
  18. ch = getchar();
  19. }
  20. while (isdigit(ch)) {
  21. x = x * 10 + (ch ^ 48);
  22. ch = getchar();
  23. }
  24. //  x *= f;
  25. }
  26. int n, k;
  27. int ans = 0;
  28. int head[maxn], top;
  29. struct E {
  30. int to, nxt, w;
  31. } edge[maxn << 1];
  32. inline void insert(int u, int v, int w) {
  33. edge[++top] = (E) {v, head[u], w};
  34. head[u] = top;
  35. }
  36. bool vis[maxn];
  37. int size[maxn], root, Mn, cur;
  38. void Find_rt(int u, int pre) {
  39. size[u] = 1;
  40. int Mxson = 0;
  41. for (int i = head[u]; i; i = edge[i].nxt) {
  42. int v = edge[i].to;
  43. if (v == pre || vis[v]) continue;
  44. Find_rt(v, u);
  45. size[u] += size[v];
  46. Mxson = max(Mxson, size[v]);
  47. }
  48. Mxson = max(Mxson, cur - size[u]);
  49. if (Mn > Mxson)
  50. root = u, Mn = Mxson;
  51. }
  52. int chd[maxn], tot;
  53. void calc(int u, int pre, int d) {
  54. chd[++tot] = d;
  55. if (d >= k) return;
  56. for (int i = head[u]; i; i = edge[i].nxt) {
  57. int v = edge[i].to;
  58. if (v == pre || vis[v]) continue;
  59. calc(v, u, d + edge[i].w);
  60. }
  61. }
  62. int Solve(int u, int extra) {
  63. int ret = 0;
  64. tot = 0;
  65. calc(u, 0, extra);
  66. sort(chd + 1, chd + 1 + tot);
  67. rint l = 1, r = tot;
  68. while (l < r)
  69. chd[l] + chd[r] <= k ? (ret += (r - l), ++l) : (--r);
  70. return ret;
  71. }
  72. void Divide(int u) {
  73. vis[u] = true;
  74. ans += Solve(u, 0);
  75. int tcur = cur;
  76. for (int i = head[u]; i; i = edge[i].nxt) {
  77. int v = edge[i].to;
  78. if (vis[v]) continue;
  79. ans -= Solve(v, edge[i].w);
  80. Mn = inf;
  81. //cur = size[v];
  82. cur = size[u] > size[v] ? size[v] : tcur - size[u];
  83. Find_rt(v, u);
  84. Divide(root);
  85. }
  86. }
  87. int main() {
  88. read(n);
  89. int u, v, w;
  90. for (int i = 1; i < n; ++i) {
  91. read(u), read(v), read(w);
  92. insert(u, v, w), insert(v, u, w);
  93. }
  94. read(k);
  95. Mn = inf, cur = n;
  96. Find_rt(1, 0);
  97. Divide(root);
  98. printf("%d", ans);
  99. return 0;
  100. }

【P4178】Tree——点分治的更多相关文章

  1. 洛谷P4178 Tree (点分治)

    题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式:   N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下 ...

  2. 洛谷 P4178 Tree —— 点分治

    题目:https://www.luogu.org/problemnew/show/P4178 这道题要把 dep( dis? ) 加入一个 tmp 数组里,排序,计算点对,复杂度很美: 没有写 sor ...

  3. P4178 Tree 点分治

    思路:点分治 提交:1次 题解: 要求权值和\(\leq K\) 的路径,我们可以类比点分治的模板,把长为\(len\)是否存在,改为\(len\)的路径的条数,并用用树状数组维护前缀和,这样就可以求 ...

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

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

  5. [Luogu P4178]Tree (点分治+splay)

    题面 传送门:https://www.luogu.org/problemnew/show/P4178 Solution 首先,长成这样的题目一定是淀粉质跑不掉了. 考虑到我们不知道K的大小,我们可以开 ...

  6. POJ1471 Tree/洛谷P4178 Tree

    Tree P4178 Tree 点分治板子. 点分治就是直接找树的重心进行暴力计算,每次树的深度不会超过子树深度的\(\frac{1}{2}\),计算完就消除影响,找下一个重心. 所以伪代码: voi ...

  7. luogu P4178 Tree

    题目链接 luogu P4178 Tree 题解 点分治 代码 // luogu-judger-enable-o2 #include<cstdio> #include<algorit ...

  8. 【题解】[P4178 Tree]

    [题解]P4178 Tree 一道点分治模板好题 不知道是不是我见到的题目太少了,为什么这种题目都是暴力开值域的桶QAQ?? 问点对,考虑点分治吧.直接用值域树状数组开下来,统计的时候直接往树状数组里 ...

  9. P4178 Tree(点分治)

    题面要求小于等于K的路径数目,我么很自然的想到点分治(不会的就戳我) 这道题的统计答案与模板题不一样的地方是由等于K到小于等于K 那么我们可以把每一个子节点到当前根(重心)的距离排序,然后用类似双指针 ...

随机推荐

  1. RocketMQ4.7.1双主双从集群搭建

    导读 上一集我们已经学会了SpringBoot整合RocketMQ点我直达,今天我们来搭建双主双从高性能MQ服务集群. 简介 主从架构 Broker角色,Master提供读写,Slave只支持读,Co ...

  2. .NET CORE QuartzJob定时任务+Windows/Linux部署

    前言 以前总结过一篇基于Quartz+Topshelf+.netcore实现定时任务Windows服务 https://www.cnblogs.com/gt1987/p/11806053.html.回 ...

  3. python数学math和random模块

    math模块 关注公众号"轻松学编程"了解更多. 在使用math模块时要先导入 # 导入模块 import math 1.math.ceil(num) 对num进行向上取整 num ...

  4. Docker(10)- docker create 命令详解

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 创建一个新的容器但不启动它 ...

  5. XJOI NOI训练2 传送

    NTT循环卷积 30分: 可以发现这是一个很明显的分层$DP$,设$dp[i][j]$表示当前走了j步走到i号节点的方案数.如果当前走的步数对节点有限制就直接将这个点的$DP$值赋成$0$ #incl ...

  6. Python调用Java(基于Ubuntu 18.04)

    最近实习,需要使用Python编程,其中牵涉到一些算法的编写.由于不熟悉Python,又懒得从头学,而且要写的算法自己之前又用Java实现过,就想着能不能用Python调用Java.经过查找资料,方法 ...

  7. 消息队列--ActiveMQ单机部署

    一.环境需求 1,Centos7环境 2,jdk 1.8+ 3,主机名的命名方式中不能含有'_'字符 二.软件包下载 1,下载路径 wget http://mirror.bit.edu.cn/apac ...

  8. 把数据转化为JSON格式用ajax进行前后端交互

    接着在https://www.cnblogs.com/dong973711/p/10907733.html的基础上做验证. 从前端提交数据 前端页面,submit.html <!DOCTYPE ...

  9. 8.字典dict和解构-封装

    字典dict 与列表的区别:列表可以存储大量的数据类型,但是只能按照顺序存储,数据与数据之间关联性不强 字典(dict)是python中唯⼀的⼀个映射类型.他是以{ }括起来的键值对组成. 字典中的键 ...

  10. Python - Git仓库忽略提交规则 & .gitignore配置

    Git 忽略文件提交的方法 有三种方法可以实现忽略Git中不想提交的文件. 在Git项目中定义 .gitignore 文件 这种方式通过在项目的某个文件夹下定义  .gitignore 文件,在该文件 ...