poj1741 树上的点分治

题意:

一棵10000个点的树,每条边的长不超过1000,给定一个值k,问距离不超过k的点对数有多少。(多组数据)

输入样例:

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

输出样例:
8

————————————————————————————————————————————————————————————————————————————
这个题目是在CODEVS 2756 树上的路径 的学习过程中看到别人的题解的过程中看到的,这个题相对简单,是做那个题的基础。基本上是人家的代码,和抄也差不了多少。是学习树上的点分治的一个基础题。
根据一个大神漆子超的论文,树上的分治可以分为点分治,边分治和链分治。点分治最差log,边分治最差n。而链分治是最好的,不过没有学会,有空再从网上找来学习一下,就是只学习点皮毛,对自己做题思路也是一种扩展。
点分治,就是在树中去掉一个点,从而使原来的树变成更小的树,从而达到分治的目的。为了防止极差情况的出现,提高分治的效率,最好的分治点是树的重心(去掉该点后,所得最大子树最小)。所在就有了点分治的方法。
1、深搜,得到各个点为根的子树的大小和各个点的最大子树的大小
2、二次深搜,得到去掉各个点得到的最大子树的大小,从而通过遍历得到重心。
3、应用问题与重心的关系进行分治。
(本题中:
3.1、计算通过重心且不超过k的点对数(包含重边)。
3.2、减去过重心且不超过k的点对数(即有重边的点对数)。
3.3、标明已查到的重心(vis),在新生成的子树中重新进行上述操作,完成分治。


边分治,没有做过,不过据漆子超说,是去掉一条过,把树分成2个树,完成分治。个人愚见,如果不是星型那种极个别的情况也可以实现log。
————————————————————————————————————————————————————————————————————————————

 

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 
  6 using namespace std;
  7 const int maxn=10010;
  8 
  9 int n,k,ans;
 10 struct edge
 11 {
 12     int u,v,w,next;
 13 }e[2*maxn];
 14 int head[maxn],js,jst,mi,root;
 15 int siz[maxn],mx[maxn],dis[maxn];
 16 bool vis[maxn];
 17 void init()
 18 {
 19     memset(head,0,sizeof(head));
 20     memset(vis,0,sizeof(vis));
 21     js=0;
 22     ans=0;
 23 }
 24 void readint(int &x)
 25 {
 26     int f=1;
 27     char c=getchar();
 28     for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-f;
 29     x=0;
 30     for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
 31     x=x*f;
 32 }
 33 void addage(int u,int v,int w)
 34 {
 35     e[++js].u=u;e[js].v=v;e[js].w=w;
 36     e[js].next=head[u];head[u]=js;
 37 }
 38 void dfssize(int u,int fa)
 39 {
 40     siz[u]=1;mx[u]=0;
 41     for(int i=head[u];i;i=e[i].next)
 42     {
 43         int v=e[i].v;
 44         if(v!=fa && !vis[v])
 45         {
 46             dfssize(v,u);
 47             siz[u]+=siz[v];
 48             if(siz[v]>mx[u])mx[u]=siz[v];
 49         }
 50     }
 51 }
 52 void dfsroot(int r,int u,int f)
 53 {
 54     if(siz[r]-siz[u]>mx[u])mx[u]=siz[r]-siz[u];
 55     if(mx[u]<mi)
 56     {
 57         mi=mx[u];
 58         root=u;
 59     }
 60     for(int i=head[u];i;i=e[i].next)
 61     {
 62         int v=e[i].v;
 63         if(v!=f &&!vis[v])
 64         {
 65             dfsroot(r,v,u);
 66         }
 67     }
 68 }
 69 void dfsdis(int u,int d,int f)
 70 {
 71     dis[jst++]=d;
 72     for(int i=head[u];i;i=e[i].next)
 73     {
 74         int v=e[i].v;
 75         if(v!=f && !vis[v])
 76         dfsdis(v,d+e[i].w,u);
 77     }
 78 }
 79 int calc(int u,int d)
 80 {
 81     int anst=0;
 82     jst=0;
 83     dfsdis(u,d,0);
 84     sort(dis,dis+jst);
 85     int i=0,j=jst-1;
 86     while(i<j)
 87     {
 88         while(dis[i]+dis[j]>k && i<j)j--;
 89         anst+=j-i;
 90         i++;
 91     }
 92     return anst;
 93 }
 94 void dfs(int u)
 95 {
 96     mi=n;
 97     dfssize(u,0);
 98     dfsroot(u,u,0);
 99     ans+=calc(root,0);
100     vis[root]=1;
101     for(int i=head[root];i;i=e[i].next)
102     {
103         int v=e[i].v;
104         if(!vis[v])
105         {
106             ans-=calc(v,e[i].w);
107             dfs(v);
108         }
109         
110     }
111     
112 }
113 int main()
114 {
115     while(scanf("%d%d",&n,&k)==2)
116     {
117         if(!n && !k)break;
118         init();
119         for(int u,v,w,i=1;i<n;i++)
120         {
121             readint(u);readint(v);readint(w);
122             addage(u,v,w);addage(v,u,w);
123         }
124         dfs(1);
125         printf("%d\n",ans);
126     }
127     return 0;
128 }
View Code

 

posted on 2016-12-12 15:30  gryzy  阅读(156)  评论(0编辑  收藏  举报

导航