点分治见BZOJ2152
此题只是同时需要把点到根的距离存到数组里, 可以用sort排序然后再统计(arr数组排序后只要arr[l]+arr[r]小于k,则arr[l]与arr中下标[l+1, r]任意一个的和都满足要求,直接统计)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 100007, inf = ~0u >> 1;
int n, k, c, csiz, tot, size[maxn], dis[maxn], arr[maxn];
int cnt, head[maxn], next[maxn << 1], to[maxn << 1], len[maxn << 1];
void addedge(int u, int v, int l) {
next[++cnt] = head[u]; to[cnt] = v;
len[cnt] = l; head[u] = cnt;
}
bool vis[maxn];
void DFS(int u, int last) {
size[u] = 1; int mxsz = 0;
for(int i = head[u]; i; i = next[i]) {
if(to[i] == last || vis[to[i]]) continue;
DFS(to[i], u);
size[u] += size[to[i]];
mxsz = max(mxsz, size[to[i]]);
}
mxsz = max(mxsz, tot - size[u]);
if(csiz > mxsz) csiz = mxsz, c = u;
}
void Dist(int u, int last) {
arr[++arr[0]] = dis[u];
for(int i = head[u]; i; i = next[i]) {
if(vis[to[i]] || to[i] == last) continue;
dis[to[i]] = dis[u] + len[i];
Dist(to[i], u);
}
}
int Calc(int u, int l) {
dis[u] = l; arr[0] = 0;
Dist(u, 0);
sort(arr + 1, arr + arr[0] + 1);
int ret = 0;
for(int i = 1, j = arr[0]; i < j;) {
if(arr[i] + arr[j] > k) j--;
else ret += j - i, i++;
}
return ret;
}
int ret;
void get_ans(int u) {
tot = size[u] ? size[u] : n;
csiz = inf; DFS(u, 0); vis[u = c] = 1;
ret += Calc(u, 0);
for(int i = head[u]; i; i = next[i]) {
if(vis[to[i]]) continue;
ret -= Calc(to[i], len[i]);
get_ans(to[i]);
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i < n; i++) {
int u, v, l; scanf("%d%d%d", &u, &v, &l);
addedge(u, v, l); addedge(v, u, l);
}
scanf("%d", &k);
get_ans(1);
printf("%d\n", ret);
return 0;
}