此题的题意是:给定一棵N个结点的带权树,定义dist(v,u)为vu,两点间的最短路径长度,路径的长度定义为路径上所有边的权和。再给定一个k,如果对于不同的两个结点a,b,,如果满足dist(a,b) <= k , 则称(a,b)为合法点对。求合法点对数。
此题的思路主要是借鉴这篇论文:http://wenku.baidu.com/view/e087065f804d2b160b4ec0b5.html
看了论文之后也不是特别理解,然后又看了这篇代码http://www.cnblogs.com/andre0506/archive/2012/10/12/2721113.html,才理解了,不过处理的时候我个人觉得他有一点点错误,那就是在求拆掉一点能得到的最大数的点个数不是他所写的max(size[i], tp[s].sum-size[i]),而应该是max(size[i],tp[s].asum-tp[point[i]].asum);其中size[i]记录的是拆掉此点后以此点的儿子节点作为根节点的最大树的点个数。
以下为具体代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <stack>
using namespace std;
#define M 10005
#define ll long long
#define int64 __int64
struct node
{
int ev, p;
int asum , lsum;//sum代表以此点为根节点的整棵树节点个数,lsum为以此点的儿子节点为根节点的最多节点的树的节点个数
node *next;
}*head[M], tree[2*M], tp[M];
int n, k, cnt, ans, tot, vis[M], size[M], point[M];
void init()
{
memset(head, 0, sizeof head);
memset(vis, 0, sizeof vis);
cnt = ans = 0;
}
void addedge(int s, int e, int p)
{
tree[cnt].ev = e;
tree[cnt].p = p;
tree[cnt].next = head[s];
head[s] = &tree[cnt++];
}
void dfs(int s , int fa)
{
tp[s].asum = 1;
tp[s].lsum = 0;
node *p = head[s];
while (p)
{
if (p->ev != fa && !vis[p->ev])
{
dfs(p->ev,s);
tp[s].asum += tp[p->ev].asum;
tp[s].lsum = max(tp[p->ev].asum , tp[s].lsum);
}
p = p->next;
}
point[tot] = s;
size[tot++] = tp[s].lsum;
}
int getroot(int s)
{
tot = 0;
dfs(s,0);
int i , root , lsum = 1<<30;
for (i = 0 ; i < tot ; i++)
{
size[i] = max(size[i],tp[s].asum-tp[point[i]].asum);//与上一篇代码的差别
if (lsum > size[i])
{
lsum = size[i];
root = point[i];
}
}
return root;
}
void getdis(int s , int fa , int dis)//size重复利用,保留的是dist
{
size[tot++] = dis;
node *p = head[s];
while (p)
{
if (p->ev != fa && !vis[p->ev] && dis+p->p <= k)
getdis(p->ev , s , dis+p->p);
p = p->next;
}
}
void count1(int root)//计算所有经过根节点并且满足条件的路径的个数
{
tot = 0;
getdis(root , 0 , 0);
sort(size , size+tot);
int l = 0 , r = tot-1;
while (l < r)
{
if (size[l]+size[r] <= k)
{
ans += r-l;
l++;
}
else r--;
}
}
void count2(int root)//删除节点在同一颗子树的情况
{
vis[root] = 1;
node *p = head[root];
while (p)
{
if (!vis[p->ev])
{
tot = 0;
getdis(p->ev , root , p->p);
sort(size , size+tot);
int l = 0 , r = tot-1;
while (l < r)
{
if (size[l]+size[r] <= k)
{
ans -= r-l;
l++;
}
else r--;
}
}
p = p->next;
}
}
void solve(int s , int fa)
{
int root = getroot(s);
count1(root);
count2(root);
node *p = head[root];
while (p)
{
if (p->ev != fa && !vis[p->ev])
{
solve(p->ev,root);
}
p = p ->next;
}
}
int main()
{
while (scanf("%d%d",&n,&k) , n+k)
{
init();
int i, s, e, p;
for (i = 1 ; i < n ; i++)
{
scanf("%d%d%d",&s,&e,&p);
addedge(s,e,p);
addedge(e,s,p);
}
solve(1,0);//这里可以以任意一个点开始搜索,因为不论从哪个点开始,重心必然是一样的
printf("%d\n",ans);
}
return 0;
}