您的位置:首页 > 其它

poj 1741 bzoj1468 Tree

2016-02-28 14:55 435 查看
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).

Define dist(u,v)=The min distance between node u and v.

Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.

Write a program that will count how many pairs which are valid for a given tree.

Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three
integers u,v,l, which means there is an edge between node u and v of length l.

The last test case is followed by two zeros.

Output
For each test case output the answer on a single line.
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output
8

Source
LouTiancheng@POJ

题目大意:求树上距离小于等于K的点对有多少个。

一道点分治题。

一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

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

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

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define INF 0x7f7f7f7f
using namespace std;
int n,K,cnt,sum,ans,root;
int head[10005],deep[10005],d[10005],f[10005],son[10005];
bool vis[10005];
struct data{int to,next,v;}e[20005];
inline int ReadInt()
{
int f = 1,x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}

void ins(int u, int v, int w)
{
e[++ cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
e[cnt].v = w;
}
void insert(int u, int v, int w)
{
ins(u,v,w);
ins(v,u,w);
}

void getroot(int x, int fa)
{
son[x] = 1;
f[x] = 0;
for(int i = head[x]; i; i = e[i].next)
{
if(e[i].to == fa || vis[e[i].to]) continue;
getroot(e[i].to, x);
son[x] += son[e[i].to];
f[x] = max(f[x], son[e[i].to]);
}
f[x] = max(f[x],sum - son[x]);
if(f[x] < f[root]) root = x;
}

void getdeep(int x, int fa)
{
deep[++ deep[0]] = d[x];
for(int i = head[x]; i; i = e[i].next)
{
if(e[i].to == fa || vis[e[i].to]) continue;
d[e[i].to] = d[x] + e[i].v;
getdeep(e[i].to, x);
}
}

int cal(int x, int y)
{
d[x] = y;
deep[0] = 0;
getdeep(x,0);
sort(deep + 1, deep + deep[0] + 1);
int t = 0, l, r;
for(l = 1, r = deep[0]; l < r; )
{
if(deep[l] + deep[r] <= K) { t += r - l; l ++;}
else r --;
}
return t;
}

void Doit(int x)
{
ans += cal(x, 0);
vis[x] = 1;
for(int i = head[x]; i; i = e[i].next)
{
if(vis[e[i].to]) continue;
ans -= cal(e[i].to, e[i].v);
sum = son[e[i].to];
root = 0;
getroot(e[i].to, root);
Doit(root);
}
}
int main()
{
while(1)
{
ans = 0;
root = 0;
cnt = 0;
memset(head, 0, sizeof(head));
memset(vis, 0, sizeof(vis));
n = ReadInt();
K = ReadInt();
if(n == 0 && K == 0) break;
for(int i = 1; i < n; i ++)
{
int u,v,w;
u = ReadInt();
v = ReadInt();
w = ReadInt();
insert(u,v,w);
}
sum = n;
f[0] = INF;
getroot(1, 0);
Doit(root);
printf("%d\n",ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj 点分治