题目分析
这道题是挑战上的一道题目,挑战上面代码写的很长并且不容易理解。此题也是楼教主男人八题之一。不过随着算法竞赛的难度逐年增加,以前的论文题现在也变成了模板题,关于点分治的问题很多,区域赛也有出过。
这道题具体方法是这样的,我们首先对树找重心,重心不理解的可以去查资料。然后根据重心对树进行分治,我们计算到树根距离小于等于K的节点,然后计算有多少对,但是我们会发现我们子树进行分治时这些点被重复计算,于是我们可以需要减去同在一个子树上的节点,只计算在不在同一个子树上的顶点对。然后一个递归算完即可。因为中间有排序时间复杂度为 O(n∗log2n) ,如果还有什么不明白请看代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+100;
struct Edge{
int to, next, val;
}e[maxn<<1];
int head[maxn], tot, n, k, ans = 0; //前向星, 题目输入的值
int vis[maxn], siz[maxn], num[maxn], dep[maxn], S[maxn], root, top, tot_size;
// 中心, 子树节点 子节点数量最大值 距根节点距离
void addedge(int from, int to, int val){
e[tot].to = to;
e[tot].val = val;
e[tot].next = head[from];
head[from] = tot++;
}
void get_root(int u, int fa){ //寻找重心
siz[u] = 1;
num[u] = 0;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(vis[v] || v == fa) continue;
get_root(v, u);
siz[u] += siz[v];
num[u] = max(num[u], siz[v]);
}
num[u] = max(num[u], tot_size-siz[u]);
if(num[u] < num[root]) root = u;
}
void get_dep(int u, int fa){
if(dep[u] <= k) S[top++] = dep[u];
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(vis[v] || v == fa) continue;
dep[v] = dep[u] + e[i].val;
get_dep(v, u);
}
}
int count_pair(int u, int val){
top = 0;
dep[u] = val;
get_dep(u, -1);
sort(S, S+top);
int l = 0, r = top-1, res = 0;
while(l < r){
if(S[l] + S[r] <= k){
res += r-l;
++l;
}
else --r;
}
return res;
}
void dfs(int u){ //分治
vis[u] = 1; //该点被标记为重心
ans += count_pair(u, 0); //计算所有点到该重心距离不超过K的所有值
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(vis[v]) continue;
ans -= count_pair(v, e[i].val);
root = 0;
tot_size = siz[v];
get_root(v, -1);
dfs(root);
}
}
void init(){
memset(vis, 0, sizeof(vis));
memset(head, -1, sizeof(head));
root = tot = 0;
num[0] = tot_size = n;
}
void solve(){
init();
int from, to, val;
for(int i = 1; i < n; i++){
scanf("%d%d%d", &from, &to, &val);
addedge(from, to, val);
addedge(to, from, val);
}
ans = 0;
get_root(1, -1);
dfs(1);
printf("%d\n", ans);
}
int main(){
while(scanf("%d%d", &n, &k) != EOF && (n||k)) solve();
return 0;
}