ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【P4178】Tree——点分治

2019-07-29 23:04:36  阅读:165  来源: 互联网

标签:ch Mxson cur int 分治 Tree edge P4178 size


(题面来自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.   
  54. void calc(int u, int pre, int d) {  
  55.     chd[++tot] = d;  
  56.     if (d >= k) return;  
  57.     for (int i = head[u]; i; i = edge[i].nxt) {  
  58.         int v = edge[i].to;  
  59.         if (v == pre || vis[v]) continue;  
  60.         calc(v, u, d + edge[i].w);  
  61.     }  
  62. }  
  63. int Solve(int u, int extra) {  
  64.     int ret = 0;  
  65.     tot = 0;  
  66.     calc(u, 0, extra);  
  67.     sort(chd + 1, chd + 1 + tot);  
  68.     rint l = 1, r = tot;  
  69.     while (l < r)  
  70.         chd[l] + chd[r] <= k ? (ret += (r - l), ++l) : (--r);  
  71.     return ret;  
  72. }  
  73. void Divide(int u) {  
  74.     vis[u] = true;  
  75.     ans += Solve(u, 0);  
  76.     int tcur = cur;  
  77.     for (int i = head[u]; i; i = edge[i].nxt) {  
  78.         int v = edge[i].to;  
  79.         if (vis[v]) continue;  
  80.         ans -= Solve(v, edge[i].w);  
  81.         Mn = inf;  
  82.         //cur = size[v];  
  83.         cur = size[u] > size[v] ? size[v] : tcur - size[u];  
  84.         Find_rt(v, u);  
  85.         Divide(root);  
  86.     }  
  87. }  
  88. int main() {  
  89.     read(n);  
  90.     int u, v, w;  
  91.     for (int i = 1; i < n; ++i) {  
  92.         read(u), read(v), read(w);  
  93.         insert(u, v, w), insert(v, u, w);  
  94.     }  
  95.     read(k);  
  96.     Mn = inf, cur = n;  
  97.     Find_rt(1, 0);  
  98.     Divide(root);  
  99.     printf("%d", ans);  
  100.     return 0;  
  101. }  

 

 

标签:ch,Mxson,cur,int,分治,Tree,edge,P4178,size
来源: https://www.cnblogs.com/TY02/p/11267061.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有