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
例题:
题意:一棵有n个节点的树,每条边有个权值代表相邻2个点的距离,要求求出所有距离不超过k的点对(u,v)
点分治算法的过程:
1)找出树的重心
①计算以u为根的树中每棵子树的大小
②根据子树大小找出树的重心root(以树的重心为根的树,可以使其根的子树中节点最多的子树的节点最少)
2)将树的重心作为根节点root,计算树中每个点到root的距离dir
3)计算树中所有满足dir[u]+dir[v]<=k的点对数cnt1
4)计算以root的子节点为根的子树中,满足dir[u]+dir[v]<=k的点对数cnt2
5)ans+=cnt1-cnt2
6)删掉节点root,分别遍历root的子树,回到第1)步
分析一下这个算法,我们每次都寻找到一个重心,把树较为平均地分为若干个子树。然后计算每个点到重心的距离,保存在dis数组里,然后用经典算法计算出和不超过k的组数。这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,因此要把它们减掉。
在这之后再对每一个子树找重心,求解,就可以求出所有的满足条件的顶点对了。
#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=11111, maxm=55555;
struct edge
{
int v, next, w;
}edge[maxm];
int e, n, k, ans, root, num, mi;
int head[maxm], dis[maxn], vis[maxn], size[maxn], mx[maxn];
void init(){
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
e = ans = 0;
}
void add(int x, int y, int z){
edge[e].v=y;
edge[e].w=z;
edge[e].next=head[x];
head[x]=e++;
}
void dfssize(int u, int fa){ //处理子树的大小
size[u] = 1; //根为u的子树的大小
mx[u] = 0; //u的最大子树的大小
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v=edge[i].v;
if (v != fa && !vis[v]) //结点v没有搜索过
{
dfssize(v, u);
size[u] += size[v];
if (size[v] > mx[u]) mx[u] = size[v];
}
}
}
void dfsroot(int r, int u, int fa){ //求重心
if (size[r] - size[u] > mx[u])
mx[u] = size[r] - size[u]; //u上面的树的点数比u的最大子树的点数大就更新mx[u]
if (mx[u] < mi) mi = mx[u], root = u; //最大子树最小的顶点就是重心
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (v != fa && !vis[v]) dfsroot(r, v, u);
}
}
void dfsdis(int u, int d, int fa) //求各顶点到重心的距离
{
dis[num++] = d;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u);
}
}
int calc(int u, int d)
{
int ret = 0;
num = 0;
dfsdis(u, d, 0);
sort(dis, dis + num); //求所有顶点中满足dis[i]+dis[j]<=k的组数
int i = 0, j = num - 1;
while (i < j) //经典
{
while (dis[i] + dis[j] > k && i < j) j--;
ret += j-i;
i++;
}
return ret;
}
void dfs(int u){
mi = n;
dfssize(u, 0); //求子树的大小
dfsroot(u, u, 0); //求重心
ans += calc(root, 0); //ans+=所有顶点中满足dis[i]+dis[j]<=k的组数
vis[root] = 1;
for (int i = head[root]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (!vis[v])
{
ans -= calc(v, edge[i].w); //减去子树中dis和小于等于k的组数
dfs(v);
}
}
}
int main()
{
int x, y, z;
while (scanf("%d%d", &n, &k)!=EOF){
if (n==0 && k==0) break;
init();
for (int i = 1; i < n; i++)
{
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
dfs(1);
printf("%d\n", ans );
}
return 0;
}