首页 > 代码库 > POJ 1741
POJ 1741
题意就是求树上距离小于等于K的点对有多少个
n2的算法肯定不行,因为1W个点
这就需要分治。可以看09年漆子超的论文
本题用到的是关于点的分治。
一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心
找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。
最后的复杂度是n logn logn 其中每次快排是nlogn 而递归的深度为logn。
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) //处理子树的大小 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) //求距离 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); 89 ans += calc(root, 0); 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); 97 dfs(v); 98 } 99 } 100 } 101 int main() 102 { 103 while(scanf("%d%d", &n, &k) != EOF) 104 { 105 if(!n && !k) break; 106 init(); 107 int u, v, w; 108 for(int i = 0; i < n - 1; i++) 109 { 110 scanf("%d%d%d", &u, &v, &w); 111 add(u, v, w); 112 add(v, u, w); 113 } 114 dfs(1); 115 printf("%d\n", ans); 116 } 117 return 0; 118 }
另附网址:09年漆子超论文 提取密码:95tu
POJ 1741
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。