
题目链接【http://poj.org/problem?id=1741】
题意:
给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数。
题解:
这是一个很经典的树分治的题。假设我们选择了一个参考点u,那么对于不同的点对(u,v),(u , v)之间的路径有两种情况,经过点u,和不经过点u,加入我算出了没有经过点u的对数,然后把经过点u的加起来就是答案了,很简单,这就是分治的思想。具体看代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e4 + ;
int N, lit;
struct Edge
{
int to, next, len;
Edge() {}
Edge(int to, int next, int len): to(to), next(next), len(len) {}
} E[maxn * ];
int head[maxn], tot;
void initEdge()
{
for(int i = ; i <= N + ; i++) head[i] = -;
tot = ;
}
void addEdge(int u, int v, int len)
{
E[tot] = Edge(v, head[u], len);
head[u] = tot++; E[tot] = Edge(u, head[v], len);
head[v] = tot++;
}
int vis[maxn];
int dep[maxn], L, R;
int sz[maxn];
void GetSize(int u, int fa)
{
sz[u] = ;
for(int k = head[u]; ~k; k = E[k].next)
{
int v = E[k].to;
if(v == fa || vis[v]) continue;
GetSize(v, u);
sz[u] += sz[v];
}
}
void GetRoot(int u, int fa, int tot, int &rt)
{
int ma = tot - sz[u];
if(ma > tot / ) return ;
for(int k = head[u]; ~k; k = E[k].next)
{
int v = E[k].to;
if(v == fa || vis[v]) continue;
GetRoot(v, u, tot, rt);
ma = max(ma, sz[v]);
}
if(ma <= tot / ) rt = u;
}
void GetPath(int u, int fa, int len)
{
dep[R++] = len;
for(int k = head[u]; ~k; k = E[k].next)
{
int v = E[k].to;
if(v == fa || vis[v]) continue;
GetPath(v, u, len + E[k].len);
}
}
int GetNum(int L, int R)
{
int ret = ;
int pos = R - ;
sort(dep + L, dep + R);
for(int i = L; i < R; i++)
{
while(pos > i && dep[i] + dep[pos] > lit) pos--;
if(pos > i) ret += pos - i;
else break;
}
return ret;
}
int GetAns(int u)
{
int ret = , rt = ;
GetSize(u, -);
GetRoot(u, -, sz[u], rt);//找到重心
vis[rt] = ;//重心为分界点
for(int k = head[rt]; ~k; k = E[k].next)
{
int v = E[k].to;
if(vis[v]) continue;
ret += GetAns(v);//不过rt点的个数
}
L = R = ;
for(int k = head[rt]; ~k; k = E[k].next)
{
int v = E[k].to;
if(vis[v]) continue;
GetPath(v, rt, E[k].len);
ret -= GetNum(L, R);
L = R;
}
ret += GetNum(, R);
for(int i = ; i < R; i++)
if(dep[i] <= lit) ret++;
else break;
vis[rt] = ;
return ret;
}
int main ()
{
while(~scanf("%d %d", &N, &lit))
{
if(N == && lit == ) break;
initEdge();
for(int i = ; i < N; i++)
{
int u, v, len;
scanf("%d %d %d", &u, &v, &len);
addEdge(u, v, len);
}
int ans = GetAns();
printf("%d\n", ans);
}
return ;
}