POJ 1741 树上 点的 分治

题意就是求树上距离小于等于K的点对有多少个

n2的算法肯定不行,因为1W个点

这就需要分治。可以看09年漆子超的论文

本题用到的是关于点的分治。

一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。

每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度是logn 

友情链接:~~http://blog.csdn.net/sdj222555/article/details/7893862 

  1 #include <iostream>  
  2 #include <algorithm>  
  3 #include <cstring>  
  4 #include <string>  
  5 #include <cstdio>  
  6 #include <cmath>  
  7 #include <queue>  
  8 #include <map>  
  9 #include <set>  
 10 #define eps 1e-5  
 11 #define MAXN 11111  
 12 #define MAXM 55555  
 13 #define INF 1000000000  
 14 using namespace std;  
 15 struct EDGE  
 16 {  
 17     int v, next, w;  
 18 }edge[MAXM];  
 19 int head[MAXN], e;  
 20 int n, k, vis[MAXN], ans, root, num;  
 21 void init()  // 清空初始值
 22 {  
 23     memset(vis,0,sizeof(vis));  
 24     memset(head,-1,sizeof(head));  
 25     e=ans=0;  
 26 }  
 27 void add(int u,int v,int w)  // 边表  加边
 28 {  
 29     edge[e].v=v;  
 30     edge[e].w=w;  
 31     edge[e].next=head[u];  
 32     head[u]=e++;  
 33 }  
 34 int mx[MAXN], size[MAXN], mi, dis[MAXN];  
 35 void dfssize(int u, int fa) //处理以u为顶的子树的大小  fa是其父节点
 36 {  
 37     size[u] = 1;  
 38     mx[u] = 0;  
 39     for(int i = head[u]; i != -1; i = edge[i].next)  
 40     {  
 41         int v = edge[i].v;  
 42         if(v != fa && !vis[v])  
 43         {  
 44             dfssize(v, u);  
 45             size[u] += size[v];  
 46             if(size[v] > mx[u]) mx[u] = size[v];  
 47         }  
 48     }  
 49 }  
 50 void dfsroot(int r,int u,int fa)//求重心所谓重心是指删去该点后
 51 {  //所形成的子树的节点数最大的最小
 52     if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];  
 53     if(mx[u]<mi) mi=mx[u],root=u;  
 54     for(int i=head[u];i!=-1;i=edge[i].next)  
 55     {  
 56         int v=edge[i].v;  
 57         if(v!=fa&&!vis[v]) dfsroot(r,v,u);  
 58     }  
 59 }  
 60 void dfsdis(int u, int d, int fa) //求所有点到达重心的距离  即dis
 61 {  
 62     dis[num++] = d;  
 63     for(int i = head[u]; i != -1; i = edge[i].next)  
 64     {  
 65         int v = edge[i].v;  
 66         if(v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u);  
 67     }  
 68 }  
 69 int calc(int u,int d)  
 70 {  
 71     int ret=0;  
 72     num=0;  
 73     dfsdis(u,d,0);  
 74     sort(dis,dis+num);  
 75     int i=0,j=num-1;  
 76     while(i<j) //经典  
 77     {  
 78         while(dis[i]+dis[j] > k && i < j) j--;  
 79         ret+=j-i;  
 80         i++;  
 81     }  
 82     return ret;  
 83 }  
 84 void dfs(int u)  
 85 {  
 86     mi = n;  
 87     dfssize(u, 0);  // 子树大小
 88     dfsroot(u, u, 0);  // 重心  求完之后  root 即为重心
 89     ans+=calc(root,0);//经过root的并且满足要求的点对数(这时候会出现重边)
 90     vis[root]=1;  
 91     for(int i = head[root]; i != -1; i = edge[i].next)  
 92     {  
 93         int v=edge[i].v;  
 94         if(!vis[v])  
 95         {  
 96             ans-=calc(v,edge[i].w);//v是root的son 以v,edge[i].w继续向下深搜
 97             //若这样还是满足要求(经过son并且满足要求的点对数,
 98             //这就是重边的情况,这时将它减掉)
 99             dfs(v);//继续处理root的son,情况同上  
100         }  
101     }  
102 }  
103 int main()  
104 {  
105     while(scanf("%d%d", &n, &k) != EOF)  
106     {  
107         if(!n && !k) break;  // 不到终止条件
108         init();  
109         int u, v, w;  
110         for(int i = 0; i < n - 1; i++)  
111         {  
112             scanf("%d%d%d", &u, &v, &w);  
113             add(u, v, w);  
114             add(v, u, w);  
115         }  
116         dfs(1);  
117         printf("%d
", ans);  
118     }  
119     return 0;  
120 }

附上图解:(图是手绘的,见谅)

当一遍处理的时候 ,root等于1,这时候ans会把5-1-2这样的点对加进去,所以我们以2,dis[2](dis[2]=1)再深搜,若是再这样的条件下还满足条件(过2节点,【此时dis值没变】,还满足小于等于7,说明他就是重边,应该减掉,而对于2-5这样合法的会在dfs[2]的时候,处理好)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 using namespace std;
 6 #define MAXN 11111
 7 #define MAXM 55555
 8 struct EDGE{
 9     int v,w,next;
10 }edge[MAXM]; 
11 int n,k,vis[MAXN],mx[MAXN],e,ans;
12 int root,num,dis[MAXN],head[MAXN],size[MAXN],mi;
13 void init(){
14     memset(vis,0,sizeof(vis));
15     memset(head,-1,sizeof(head));
16     ans=e=0;
17 }
18 void add(int u,int v,int w){
19     edge[e].v=v;edge[e].w=w;
20     edge[e].next=head[u];head[u]=e++;
21 }
22 void dfssize(int u,int fa){
23     size[u]=1;mx[u]=0;
24     for(int i=head[u];i!=-1;i=edge[i].next){
25         int v=edge[i].v;
26         if(v!=fa&&!vis[v]){
27             dfssize(v,u);
28             size[u]+=size[v];
29             if(size[v]>mx[u]) mx[u]=size[v];
30         }
31     }
32 }
33 void dfsroot(int r,int u,int fa){
34     if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
35     if(mx[u]<mi){ mi=mx[u];root=u; }
36     for(int i=head[u];i!=-1;i=edge[i].next){
37         int v=edge[i].v;
38         if(v!=fa&&!vis[v]){
39             dfsroot(r,v,u);
40         }
41     }
42 }
43 void dfsdis(int u,int d,int fa){
44     dis[num++]=d;
45     for(int i=head[u];i!=-1;i=edge[i].next){
46         int v=edge[i].v;
47         if(v!=fa&&!vis[v]){
48             dfsdis(v,d+edge[i].w,u);
49         }
50     }
51 }
52 int calc(int u,int d){
53     int ret=0;
54     num=0;
55     dfsdis(u,d,0);sort(dis,dis+num);
56     int i=0,j=num-1;
57     while(i<j){
58         while(dis[i]+dis[j]>k&&i<j) j--;
59         ret+=j-i;
60         i++;
61     }
62     return ret;
63 }
64 void dfs(int u)
65 {
66     mi=n;
67     dfssize(u,0);dfsroot(u,u,0);
68     ans+=calc(root,0);
69     vis[root]=1;
70     for(int i=head[root];i!=-1;i=edge[i].next){
71         int v=edge[i].v;
72         if(!vis[v]){
73             ans-=calc(v,edge[i].w);
74             dfs(v);
75         }
76     }
77 }
78 int main()
79 {
80     while(scanf("%d%d",&n,&k)!=EOF)
81     {
82         if(!n&&!k) break;
83         init();int u,v,w;
84         for(int i=1;i<n;i++){
85             scanf("%d%d%d",&u,&v,&w);
86             add(u,v,w);add(v,u,w);
87         }
88         dfs(1);
89         printf("%d
",ans);
90     }
91     return 0;
92 }

另附网址:09年漆子超论文  提取密码:95tu

原文地址:https://www.cnblogs.com/suishiguang/p/6139299.html