题目:http://poj.org/problem?id=1741
题意:给定一棵树,有n个点和n - 1条边,边有边权,给出一个K,求树上任意两点间的最短距离不大于K的个数
思路:看了09年漆子超的论文,利用分治法,每次找树的重心,重心即是在当前树中删掉此点后,节点数最多的子树的节点数最小,找到重心后,求其所有子孙到其的距离,然后统计一下两两之和不大于K的个数,另外在统计过程中,会把在同一分支中的两点也计算了,即这两点间的路径不经过重心,这明显是多余的,所以要减掉
总结:树上的算法真是巧妙啊。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
struct edge
{
int to, val, next;
}g[N*2];
int head[N], siz[N], sonsiz[N], dis[N];
bool vis[N];
int n, k, root, res, tmp, num, cnt;
void add_edge(int v, int u, int val)
{
g[cnt].to = u;
g[cnt].val = val;
g[cnt].next = head[v];
head[v] = cnt++;
}
void dfssiz(int v, int fa) /*处理以v点为根的树的节点个数和v的子树中节点最多的子树节点数*/
{
siz[v] = 1, sonsiz[v] = 0;
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fa && ! vis[u])
{
dfssiz(u, v);
siz[v] += siz[u];
sonsiz[v] = max(sonsiz[v], siz[u]);
}
}
}
void dfsroot(int r, int v, int fa) /*寻找重心*/
{
sonsiz[v] = max(sonsiz[v], siz[r] - siz[v]); /*以v为重心时,节点最多的子树节点数*/
if(sonsiz[v] < tmp) tmp = sonsiz[v], root = v; /*更新重心*/
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fa && ! vis[u]) dfsroot(r, u, v);
}
}
void dfsdis(int v, int d, int fa) /*计算重心到每个子孙的距离*/
{
dis[num++] = d;
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fa && ! vis[u]) dfsdis(u, d + g[i].val, v);
}
}
int calc(int v, int d)
{
int ans = 0;
num = 0;
dfsdis(v, d, 0);
sort(dis, dis + num);
int i = 0, j = num - 1;
while(i < j) /*计算两点距离小于k的个数,很巧妙*/
{
while(dis[i] + dis[j] > k && i < j) j--;
ans += j - i;
i++;
}
return ans;
}
void dfs(int v)
{
tmp = n;
dfssiz(v, 0);
dfsroot(v, v, 0);
res += calc(root, 0);
vis[root] = true; /*标记重心,拆掉*/
for(int i = head[root]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(! vis[u])
{
res -= calc(u, g[i].val); /*多算了在同一分支上的两点,因此减去*/
dfs(u);
}
}
}
int main()
{
int a, b, c;
while(scanf("%d%d", &n, &k), n || k)
{
cnt = res = 0;
memset(head, -1, sizeof head);
memset(vis, 0, sizeof vis);
for(int i = 0; i < n - 1; i++)
{
scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
add_edge(b, a, c);
}
dfs(1);
printf("%d\n", res);
}
return 0;
}