POJ1741——Tree(树的点分治)

  1 /* ***********************************************
  2 Author        :kuangbin
  3 Created Time  :2013-11-17 14:30:29
  4 File Name     :E:\2013ACM\专题学习\树的分治\POJ1741.cpp
  5 ************************************************ */
  6 
  7 #include <stdio.h>
  8 #include <string.h>
  9 #include <iostream>
 10 #include <algorithm>
 11 #include <vector>
 12 #include <queue>
 13 #include <set>
 14 #include <map>
 15 #include <string>
 16 #include <math.h>
 17 #include <stdlib.h>
 18 #include <time.h>
 19 using namespace std;
 20 const int MAXN = 10010;
 21 const int INF = 0x3f3f3f3f;
 22 struct Edge
 23 {
 24     int to,next,w;
 25 }edge[MAXN*2];
 26 int head[MAXN],tot;
 27 void init()
 28 {
 29     tot = 0;
 30     memset(head,-1,sizeof(head));
 31 }
 32 void addedge(int u,int v,int w)
 33 {
 34     edge[tot].to = v; edge[tot].w = w;
 35     edge[tot].next = head[u];head[u] = tot++;
 36 }
 37 bool vis[MAXN];
 38 int size[MAXN],dep[MAXN];
 39 int le,ri;
 40 int dfssize(int u,int pre)
 41 {
 42     size[u] = 1;
 43     for(int i = head[u];i != -1;i = edge[i].next)
 44     {
 45         int v = edge[i].to;
 46         if(v == pre || vis[v])continue;
 47         size[u] += dfssize(v,u);
 48     }
 49     return size[u];
 50 }
 51 int minn;
 52 //找重心
 53 void getroot(int u,int pre,int totnum,int &root)
 54 {
 55     int maxx = totnum - size[u];
 56     for(int i = head[u];i != -1;i = edge[i].next)
 57     {
 58         int v = edge[i].to;
 59         if(v == pre || vis[v])continue;
 60         getroot(v,u,totnum,root);
 61         maxx = max(maxx,size[v]);
 62     }
 63     if(maxx < minn){minn = maxx; root = u;}
 64 }
 65 void dfsdepth(int u,int pre,int d)
 66 {
 67     dep[ri++] = d;
 68     for(int i = head[u];i != -1;i = edge[i].next)
 69     {
 70         int v = edge[i].to;
 71         if(v == pre || vis[v])continue;
 72         dfsdepth(v,u,d+edge[i].w);
 73     }
 74 }
 75 int k;
 76 int getdep(int a,int b)
 77 {
 78     sort(dep+a,dep+b);
 79     int ret = 0, e = b-1;
 80     for(int i = a;i < b;i++)
 81     {
 82         if(dep[i] > k)break;
 83         while(e >= a && dep[e] + dep[i] > k)e--;
 84         ret += e - a + 1;
 85         if(e > i)ret--;
 86     }
 87     return ret>>1;
 88 }
 89 int solve(int u)
 90 {
 91     int totnum = dfssize(u,-1);
 92     int ret = 0;
 93     minn = INF;
 94     int root;
 95     getroot(u,-1,totnum,root);
 96     vis[root] = true;
 97     for(int i = head[root];i != -1;i = edge[i].next)
 98     {
 99         int v = edge[i].to;
100         if(vis[v])continue;
101         ret += solve(v); 
102     }
103     le = ri = 0;
104     for(int i = head[root];i != -1;i = edge[i].next)
105     {
106         int v = edge[i].to;
107         if(vis[v])continue;
108         dfsdepth(v,root,edge[i].w);
109         ret -= getdep(le,ri);
110         le = ri;
111     }
112     ret += getdep(0,ri);
113     for(int i = 0;i < ri;i++)
114     {
115         if(dep[i] <= k)ret++;
116         else break;
117     }
118     vis[root] = false;
119     return ret;
120 }
121 
122 int main()
123 {
124     //freopen("in.txt","r",stdin);
125     //freopen("out.txt","w",stdout);
126     int n;
127     int u,v,w;
128     while(scanf("%d%d",&n,&k) == 2)
129     {
130         if(n == 0 && k == 0)break;
131         init();
132         for(int i = 1;i < n;i++)
133         {
134             scanf("%d%d%d",&u,&v,&w);
135             addedge(u,v,w);
136             addedge(v,u,w);
137         }
138         memset(vis,false,sizeof(vis));
139         printf("%d\n",solve(1));
140     }
141     return 0;
142 }

 

树的分治算法——基于点的分治

首先选取一个点将无根树转为有根树,再递归处理每一颗以根结点的儿子为根的子树。

给定一颗N(1<=N<=10000)个结点的带权树,定义dist(u,v)为u,v两点间的最短路径长度,路径的长度定义为路径上所有边的权和。

再给定一个K(1<=K<=10^9),如果对于不同的两个结点a,b,如果满足dist(a,b)<=K,则称(a,b)为合法点对。

求合法点对个数。

算法分析:

如果使用普通的DFS遍历,时间复杂度高达O(N^2),而使用时间复杂度为O(NK)的动态规划,更是无法在规定时限内出解的。

我们知道一条路径要么过根结点,要么在一颗子树中,这启发了我们可以使用分治算法。

路径在子树中的情况只需递归处理即可,下面我们来分析如何处理路径过根结点的情况。

记Depth(i)表示点i到根结点的路径长度,Belong(i)=X(X为根结点的某个儿子,且结点i在以X为根的子树内)。那么我们要统计的就是:

满足Depth(i)+Depth(j)<=K且Belong(i)!=Belong(j)的(i,j)个数

=满足Depth(i)+Depth(j)<=K的(i,j)个数

- 满足Depth(i)+Depth(j)<=K的(i,j)个数且Belong(i)==Belong(j)的(i,j)个数

而对于这两个部分,都是要求出满足Ai+Aj<=K的(i,j)的对数。将A排序后利用单调性可以得出一个O(N)的算法,所以可以用O(NlogN)的时间来解决这个问题。

综上,此题使用树的点分治算法时间复杂度为O(Nlog2N)。

所谓点分治,对于一条树路径,只有经过或不经过一个点的情况。

对于不经过的情况 把一棵树按这个点拆成好几棵分治就行了。

(楼天城男人八题,漆子超IOI2009国家集训队论文)

posted @ 2017-03-23 03:07  饼饼饼饼饼  阅读(140)  评论(0编辑  收藏  举报