Tree 点分治
题意:给一颗树,n个点,n-1条边,问有多少对点满足两点之间的距离小于k。码一道题
思路:设这个树的根为p,那么点对(x,y)分为过p点或者在p的子树部分,我们将点对按照所过的根节点进行划分成子问题处理,对于每个以p为根的子树,对于这个树,我们求d数组记录点到根的距离,b数组记录点所在根哪个子节点,之后用a数组记录节点,并把a数组按d排序,之后就可以通过双指针扫描求满足条件点对的个数。
//Author:XuHt #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 10006; int n, k, s[N], Ans; bool v[N], w[N]; int Head[N], Edge[N<<1], Leng[N<<1], Next[N<<1], t; int ans, pos; int d[N], b[N], a[N], tot, cnt[N]; void dfs_find(int S, int x) { v[x] = 1; s[x] = 1; int max_part = 0; for (int i = Head[x]; i; i = Next[i]) { int y = Edge[i]; if (v[y] || w[y]) continue; dfs_find(S, y); s[x] += s[y]; max_part = max(max_part, s[y]); } max_part = max(max_part, S - s[x]); if (max_part < ans) { ans = max_part; pos = x; } } void dfs(int x) { v[x] = 1; for (int i = Head[x]; i; i = Next[i]) { int y = Edge[i], z = Leng[i]; if (v[y] || w[y]) continue; ++cnt[b[a[++tot]=y]=b[x]]; d[y] = d[x] + z; dfs(y); } } bool cmp(int i, int j) { return d[i] < d[j]; } void work(int S, int x) { memset(v, 0, sizeof(v)); ans = S; dfs_find(S, x); memset(d, 0, sizeof(d)); memset(cnt, 0, sizeof(cnt)); memset(v, 0, sizeof(v)); w[a[tot=1]=b[pos]=pos] = 1; ++cnt[pos]; for (int i = Head[pos]; i; i = Next[i]) { int y = Edge[i], z = Leng[i]; if (v[y] || w[y]) continue; ++cnt[a[++tot]=b[y]=y]; d[y] = z; dfs(y); } sort(a + 1, a + tot + 1, cmp); int l = 1, r = tot; --cnt[b[a[1]]]; while (l < r) { while (d[a[l]] + d[a[r]] > k) --cnt[b[a[r--]]]; Ans += r - l - cnt[b[a[l]]]; --cnt[b[a[++l]]]; } int now = pos; for (int i = Head[now]; i; i = Next[i]) { int y = Edge[i]; if (w[y]) continue; work(s[y], y); } } void add(int x, int y, int z) { Edge[++t] = y; Leng[t] = z; Next[t] = Head[x]; Head[x] = t; } void Tree() { t = 0; memset(Head, 0, sizeof(Head)); for (int i = 1; i < n; i++) { int x, y, z; scanf("%d %d %d", &x, &y, &z); add(x, y, z); add(y, x, z); } memset(w, 0, sizeof(w)); Ans = 0; work(n, 1); cout << Ans << endl; } int main() { while (cin >> n >> k && n && k) Tree(); return 0; }