POJ 1741 Tree 树分治

时间:2021-10-24 04:23:35

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.

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;
}