Tree POJ - 1741 (點分治模板)
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
Sample Output
8
題意:
求樹上兩點間距離小於等於k的點的對數。
大致的解釋寫程式碼裡了, 理解透了再詳細寫一遍。。。(/逃)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 1e4 + 100; int top, num, root, Max, ans, n, k, cnt; int head[maxn], size[maxn], dist[maxn]; bool vis[maxn]; struct node { int v, w, next; }edge[maxn << 1]; inline void add(int u, int v, int w) { //鏈式前向星存樹 edge[top].v = v; edge[top].w = w; edge[top].next = head[u]; head[u] = top++; } inline void Init() { //初始化 top = 0; ans = 0; memset(vis, false, sizeof(vis)); memset(head, -1, sizeof(head)); } void dfs_root(int u, int father) { //用來找重心 size[u] = 1; //size陣列記錄以節點i為根的子樹的節點數 int ma = 0; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v, w = edge[i].w; if(v != father && !vis[v]) { dfs_root(v, u); size[u] += size[v]; ma = max(ma, size[v]); } } ma = max(ma, cnt - size[u]); if(ma < Max) { //通過比較得出重心 Max = ma; root = u; } } void dfs_dis(int u, int father, int dis) { //求以某個節點為根時,其子樹所有節點到根的距離 dist[num++] = dis; //將所有距離存放到dist陣列中 for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v, w = edge[i].w; if(v != father && !vis[v]) { dfs_dis(v, u, dis + w); } } } int calc(int u, int dis) { //計算相應子樹的答案 int res = 0; num = 0; dfs_dis(u, 0, dis); //求u的子樹中所有節點到u的距離 sort(dist, dist + num); //排序是為了方便以O(n)的複雜度解決計數問題 int i = 0, j = num - 1; while(i < j) { while(i < j && dist[i] + dist[j] > k) j --; res += j - i; i ++; } return res; } void work(int u) { Max = n; //用來求重心 dfs_root(u, 0); //找到當前子樹的重心 cnt = size[u]; ans += calc(root, 0); //計數 vis[root] = true; //分治過的節點打上標記 for(int i = head[root]; i != -1; i = edge[i].next) { int v = edge[i].v, w = edge[i].w; if(!vis[v]) { ans -= calc(v, w); //減去不合法的 work(v); } } } int main() { //freopen("in.txt", "r", stdin); while(scanf("%d%d", &n, &k) != EOF) { if(n == 0 && k == 0) break; int u, v, w; Init(); for(int i = 1; i <= n - 1; ++ i) { scanf("%d%d%d", &u, &v, &w); add(u, v, w); add(v, u, w); } work(1); cout << ans << endl; } return 0; }