题目链接【http://poj.org/problem?id=1741】
题意:
给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数。
题解:
这是一个很经典的树分治的题。假设我们选择了一个参考点u,那么对于不同的点对(u,v),(u , v)之间的路径有两种情况,经过点u,和不经过点u,加入我算出了没有经过点u的对数,然后把经过点u的加起来就是答案了,很简单,这就是分治的思想。具体看代码。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1e4 + 15; int N, lit; struct Edge { int to, next, len; Edge() {} Edge(int to, int next, int len): to(to), next(next), len(len) {} } E[maxn * 2]; int head[maxn], tot; void initEdge() { for(int i = 0; i <= N + 5; i++) head[i] = -1; tot = 0; } void addEdge(int u, int v, int len) { E[tot] = Edge(v, head[u], len); head[u] = tot++; E[tot] = Edge(u, head[v], len); head[v] = tot++; } int vis[maxn]; int dep[maxn], L, R; int sz[maxn]; void GetSize(int u, int fa) { sz[u] = 1; for(int k = head[u]; ~k; k = E[k].next) { int v = E[k].to; if(v == fa || vis[v]) continue; GetSize(v, u); sz[u] += sz[v]; } } void GetRoot(int u, int fa, int tot, int &rt) { int ma = tot - sz[u]; if(ma > tot / 2) return ; for(int k = head[u]; ~k; k = E[k].next) { int v = E[k].to; if(v == fa || vis[v]) continue; GetRoot(v, u, tot, rt); ma = max(ma, sz[v]); } if(ma <= tot / 2) rt = u; } void GetPath(int u, int fa, int len) { dep[R++] = len; for(int k = head[u]; ~k; k = E[k].next) { int v = E[k].to; if(v == fa || vis[v]) continue; GetPath(v, u, len + E[k].len); } } int GetNum(int L, int R) { int ret = 0; int pos = R - 1; sort(dep + L, dep + R); for(int i = L; i < R; i++) { while(pos > i && dep[i] + dep[pos] > lit) pos--; if(pos > i) ret += pos - i; else break; } return ret; } int GetAns(int u) { int ret = 0, rt = 0; GetSize(u, -1); GetRoot(u, -1, sz[u], rt);//找到重心 vis[rt] = 1;//重心为分界点 for(int k = head[rt]; ~k; k = E[k].next) { int v = E[k].to; if(vis[v]) continue; ret += GetAns(v);//不过rt点的个数 } L = R = 0; for(int k = head[rt]; ~k; k = E[k].next) { int v = E[k].to; if(vis[v]) continue; GetPath(v, rt, E[k].len); ret -= GetNum(L, R); L = R; } ret += GetNum(0, R); for(int i = 0; i < R; i++) if(dep[i] <= lit) ret++; else break; vis[rt] = 0; return ret; } int main () { while(~scanf("%d %d", &N, &lit)) { if(N == 0 && lit == 0) break; initEdge(); for(int i = 1; i < N; i++) { int u, v, len; scanf("%d %d %d", &u, &v, &len); addEdge(u, v, len); } int ans = GetAns(1); printf("%d\n", ans); } return 0; }