【点的分治】
已知一棵有n个点的树,以及每两个相邻点之间的距离,求两个的之间的最小距离<=k的点有多少对
可以先看一看论文《分治算法在树的路径问题中的应用 》
树的重心:删掉这个点之后,最大子树包含的节点最少
先找到重心,计算每一个点到重心的距离,用数组dis[ ]记录,我们要求的是点的对数,没必要知道是哪几个点
所以将dis[ ]排序,如果dis[i] + dis[j] <= k,i<r<j,那么dis[i] + dis[r] <= k,所以总对数ans+=(j-i)
如果一直按照这个顺序做下来,同一棵树内的两个点会算了好几遍,导致结果偏大
所以我们还要将同一棵树内的点对减去
用vector会超时
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define inf 11111
int n, k, ans, mi, root;
struct edge
{
int v, next, w;
}edge[inf*5];
int head[inf], tot;
int size[inf], mx[inf], vis[inf];
void add(int u, int v, int w)
{
edge[tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfssize(int p, int u)//求的大小
{
size[u] = 1;
mx[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(v != p && !vis[v])
{
dfssize(u, v);
size[u]+=size[v];
mx[u] = max(mx[u], size[v]);
}
}
}
void dfsroot(int p, int u, int r)//求重心
{
if(size[r] - size[u] > mx[u]) mx[u] = size[r] - size[u];
if(mi > mx[u]) mi = mx[u], root = u;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(v!=p && !vis[v])
{
dfsroot(u, v, r);
}
}
}
int dis[inf], num;
void dfsdis(int u, int p, int d)//求树上的每一个点的重心的距离
{
dis[num++] = d;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(v!=p && !vis[v])
{
dfsdis(v, u, d+edge[i].w);
}
}
}
int calc(int u, int d)//计算这个重心的点对
{
num = 0;
dfsdis(u, 0, d);
sort(dis, dis+num);
int ret = 0;
int l = 0, r = num-1;
while(l < r)
{
while(dis[l]+dis[r]>k && l<r) r--;
ret += r-l;
l++;
}
return ret;
}
void dfs(int u)
{
mi = n;
dfssize(-1, u);
dfsroot(-1, u, u);
ans += calc(root, 0);
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);
dfs(v);
}
}
}
int main()
{
while(~scanf("%d%d", &n, &k))
{
if(n==0 && k==0) break;
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
tot=0;
for(int i = 0; i < n-1; i++)
{
int a, b, d;
scanf("%d%d%d", &a, &b, &d);
add(a, b, d);
add(b, a, d);
}
ans = 0;
dfs(1);
printf("%d\n", ans);
}
return 0;
}
【边的分治】