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 }

 

posted @ 2016-04-25 21:45  Naturain  阅读(160)  评论(0)    收藏  举报