POJ 1741 Tree 树分治
Tree
Time Limit: 20 Sec
Memory Limit: 256 MB
题目连接
http://poj.org/problem?id=1741
Description
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.
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
HINT
题意
给你一颗树,问你有多少个对点的距离小于k
题解:
树分治,可以去看漆子超的论文,讲的很详细
http://wenku.baidu.com/link?url=7KOPn20aLvKK5PqDmuLjIyj4sqZ6CL1H9qP__JSGvX-AWgX7LR6gC-BZ3PTVCP2ojBHxKZcJ5U3csiRjuspqcoFJfswO7JaEIQyKlxwUzBi
代码
#include<iostream> #include<stdio.h> #include<vector> #include<cstring> #include<algorithm> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) #define maxn 10005 struct Edge{ int v,w; Edge(){} Edge(int v1,int w1):v(v1),w(w1){} bool operator<(const Edge& other) const { return w < other.w; } }; vector<Edge> e[maxn]; int n,k; int sum,root,ans,cnt; int f[maxn],vis[maxn],son[maxn],d[maxn],deep[maxn]; void init() { root = ans = cnt = 0; for(int i=0;i<maxn;i++) e[i].clear(); mem(vis,0); mem(f,0); mem(son,0); mem(d,0); mem(deep,0); } void getroot(int u,int fa) { son[u]=1;f[u]=0; for(int i=0;i<e[u].size();i++) { int v = e[u][i].v; if(v == fa || vis[v]) continue; getroot(v,u); son[u] += son[v]; f[u] = max(f[u], son[v]); } f[u]=max(f[u],sum-f[u]); //f[u] 最后求得是以u为根的最大子树的size //当前树的root 应该是重心 //重心是f[u] 最小的 if(f[u] < f[root]) root=u; } void getdeep(int u,int fa) { deep[++deep[0]] = d[u]; for(int i=0;i<e[u].size();i++) { int v = e[u][i].v, w = e[u][i].w; if(v==fa || vis[v]) continue; d[v] = d[u] + w; getdeep(v,u); } } int cal(int u,int now) { d[u]=now; deep[0]=0; getdeep(u,0); sort(deep+1,deep+deep[0]+1); int t=0,l=1,r=deep[0]; while (l < r) { if(deep[l] + deep[r] <=k) t+= r-l, l++; else r--; } return t; } void solve(int u) { //计算经过树根u 的 deep[l] + deep[r] <= k //但因为经过树根u 的配对点(l,r) 可能不用经过u能 <=k,所以应该去除 //所以每次ans应该计算的是两个不同子树的节点l,r 使得 deep[l] + deep[r] <= k ans += cal(u,0); vis[u] = 1; for(int i=0;i<e[u].size();i++) { int v = e[u][i].v, w = e[u][i].w; if(vis[v]) continue; ans -= cal(v,w); sum = son[v]; root = 0; getroot(v,root); solve(root); } } int main () { while (scanf("%d %d",&n,&k) != EOF) { if(n==0&&k==0) break; init(); for(int i=1;i<n;i++) { int u,v,w; scanf("%d %d %d",&u,&v,&w); e[u].push_back(Edge(v,w)); e[v].push_back(Edge(u,w)); } sum=n,f[0]=0x3f3f3f3f; getroot(1,0); solve(root); printf("%d\n",ans); } return 0; }