POJ--1741(点分治)
2016-04-25 21:24:12
关于树分治的学习参见IOI2009国家集训队论文(漆子超)
题意:给出一颗 n 个点的带边权树,求出距离 <= k 的点对数。
思路:初学点分治,将具体的步骤做个归纳:
概念1:树的重心:将该点删除后,结点最多的子树的结点数最少。
(1)对于一颗当前考虑的子树,在其中选出重心(core),方法是 dfs 两遍,第一遍求子树 size 数组,接着用 dp 的思想求出重心。
(2)把当前子树的答案求出,方法是把把重心提为根,求出子树内所有点的深度,然后假设所有点对都经过重心,用双指针计算符合要求的点对数。
(3)显然(2)中并不是所有符合要求的点对的路径都经过重心,所以要把这部分减掉,即重心的所有直接儿子的子树的答案。
(4)经过(2)和(3),我们已经计算掉路径经过当前重心的点对数了,接下来就是递归计算重心的儿子们了。
Add1:由于每次(2)求出重心后,至少把整棵树的点平分,所以递归深度最大为 log(n),每层的处理复杂度是 O(n + nlogn)的,因此总复杂度为 O(n * logn * logn)
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 const int MAXN = 10010; 9 10 int n,k; 11 int first[MAXN],ecnt; 12 int sz[MAXN],vis[MAXN],dep[MAXN],dtot,szmin,ans; 13 14 struct edge{ 15 int v,next,c; 16 }e[MAXN << 1]; 17 18 inline void add_edge(int u,int v,int c){ 19 e[ecnt].next = first[u]; 20 e[ecnt].v = v; 21 e[ecnt].c = c; 22 first[u] = ecnt++; 23 } 24 25 inline void Init(){ 26 memset(first,-1,sizeof(first)); 27 memset(vis,0,sizeof(vis)); 28 ecnt = 0; 29 } 30 31 void Dfs_sz(int p,int pre){ 32 sz[p] = 1; 33 for(int i = first[p]; ~i; i = e[i].next){ 34 int v = e[i].v; 35 if(v == pre || vis[v]) continue; 36 Dfs_sz(v,p); 37 sz[p] += sz[v]; 38 } 39 } 40 41 void Dfs_core(int p,int pre,int total,int &root){ 42 int tmax = total - sz[p]; 43 for(int i = first[p]; ~i; i = e[i].next){ 44 int v = e[i].v; 45 if(v == pre || vis[v]) continue; 46 Dfs_core(v,p,total,root); 47 if(sz[v] > tmax) tmax = sz[v]; 48 } 49 if(tmax < szmin){ 50 szmin = tmax; 51 root = p; 52 } 53 } 54 55 void Dfs_dep(int p,int pre,int d){ 56 dep[dtot++] = d; 57 for(int i = first[p]; ~i; i = e[i].next){ 58 int v = e[i].v; 59 if(v == pre || vis[v]) continue; 60 Dfs_dep(v,p,d + e[i].c); 61 } 62 } 63 64 int Cal(int p,int d){ 65 dtot = 0; 66 Dfs_dep(p,-1,d); 67 sort(dep,dep + dtot); 68 int res = 0,ed = dtot - 1; 69 for(int i = 0; ; ++i){ 70 while(ed > i && dep[i] + dep[ed] > k) ed--; 71 if(ed <= i) break; 72 res += ed - i; 73 } 74 return res; 75 } 76 77 void TreeDAC(int p){ 78 int root; 79 szmin = n; 80 Dfs_sz(p,-1); 81 Dfs_core(p,-1,sz[p],root); 82 // 在 p 的子树中找到 root,把 root 拎上来作为新根 83 ans += Cal(root,0); 84 vis[root] = 1; // 先算完本区域,再标记 85 for(int i = first[root]; ~i; i = e[i].next){ 86 int v = e[i].v; 87 if(vis[v]) continue; 88 ans -= Cal(v,e[i].c); 89 TreeDAC(v); 90 } 91 } 92 93 int main(){ 94 while(scanf("%d%d",&n,&k),n + k){ 95 Init(); 96 for(int i = 1; i < n; ++i){ 97 int a,b,c; 98 scanf("%d%d%d",&a,&b,&c); 99 add_edge(a,b,c); 100 add_edge(b,a,c); 101 } 102 ans = 0; 103 TreeDAC(1); 104 printf("%d\n",ans); 105 } 106 return 0; 107 }

浙公网安备 33010602011771号