题意:n(n <= 10000)个点的带权树,统计距离<= k的点对个数。
分析:
第一道点分治...
1.求出树的重心。
2.统计通过重心的点对数量。
3.调用重心的每个儿子,去重。
4.删去重心,对每个子树递归求解。
至于统计,就是排序后,两个指针乱搞就行。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10005, M = 20005;
int n,k,e,x,y,z,siz,mx,rt,ans,l,r,sz[N],v[N],d[N],dd[N],hd[N],w[M],nxt[M],to[M];
void add(int x, int y, int z) {to[++e] = y, w[e] = z, nxt[e] = hd[x], hd[x] = e;}
void dfs(int x, int p) {
sz[x] = 1; int mxx = 0;
for(int i=hd[x];i;i=nxt[i]) if(to[i]!=p&&!v[to[i]]) dfs(to[i],x),sz[x]+=sz[to[i]],mxx=max(mxx,sz[to[i]]);
mxx = max(mxx, siz-sz[x]);
if(mxx < mx) mx = mxx, rt = x;
}
void dfs2(int x, int p) {
dd[e++] = d[x];
for(int i=hd[x];i;i=nxt[i]) if(to[i]!=p&&!v[to[i]]) d[to[i]]=d[x]+w[i],dfs2(to[i],x);
}
int calc(int x, int y) {
e = z = l = 0, d[x] = y, dfs2(x, 0);
sort(dd, dd+e), r = e-1;
while(l < r) if(dd[l]+dd[r] <= k) z += r-l, l++; else r--;
return z;
}
void lty(int x) {
ans += calc(x, 0), v[x] = 1;
for(int i = hd[x]; i; i = nxt[i]) if(!v[to[i]])
ans -= calc(to[i], w[i]), mx = siz = sz[to[i]], dfs(to[i], 0), lty(rt);
}
int main() {
while(scanf("%d%d", &n, &k) && n) {
e = ans = 0, siz = mx = n;
memset(hd, 0, sizeof hd);
memset(v, 0, sizeof v);
for(int i = 1; i < n; i++) scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
dfs(1, 0), lty(rt);
printf("%d\n", ans);
}
return 0;
}