poj1741 Tree 树的点分冶

Tree
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 13528   Accepted: 4350

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

Source

LouTiancheng@POJ

[Submit]   [Go Back]   [Status]   [Discuss

题意,是给出一个树,要求距离小于k的结点对数,树的点分冶。

由容质原理,一棵树的结点对数为  

没有限制的 - ∑端点落在同一颗子树上的. 上述两个过程是一样的, 于是只考虑没有限制的怎么做。所以用树的点分冶,统计经过树根的个数,然后,分别统计经过,每个结点的个数,反正,对于一棵树,不论以谁看成是根结点,都是可以的。那么,找那个结点作为根最好呢,当然是树的重心。

1.求树的重心,就是有个结点,以它为根的话,它所有的子树的最大结点最小,用树形dp的做法,可以在o(n)复杂度内解决。

dp[i]表示,以i为根,子树的结点数的最大值。则dp[i] = max(dp[j],总结点数 - nodes[i]);j是i的子树,nodes[i]表示i为根,所有的结点数。以i为根的时候,还要考虑到我们是以i的根搜下来的,所以要用总是减去i为根的总结点数,就是以i的父结点为i子结点时的总个数。

2.统计以i为根没有限制的对数,我们把所以的i的树上的结点到i的距离,用一个深搜统计起来,再加入一个数组,排下序,转化成了,要在一个排了序的数组a1 a2  ....  an 求出ai + a[j] <=k(j > i)的个数,这里,就可以用o(n)方法统计出来。枚举每个i,因为a[i]是递增的,所以j就是递减的。由于j只会减不会增,所以,是线性算法。端点落在同一颗子树上的和没有限制的是同一个问题,只要以子结点为根统计,就可以了。

总的说一下,由于每次是以重心为根,那么子结点的个数一定就是减少了一半的,所以在log(n)可以解决,对于一棵树,由于,每个树都要统计排序,复杂度为n * log(n),所以总的复杂度为o(n * log(n) * log(n));也就可以过了。

#define N 10050
#define M 100005
#define maxn 205
#define MOD 1000000000000000007
int n,m,k,u,v,l,center,all,num,nodes[N],dp[N],dep[N];
ll ans;
bool vis[N];
vector<pii> p[N];
vector<int> depV;
void findRoot(int root,int fa){
    nodes[root] = 1;dp[root] = 0;
    FI(p[root].size()){
        int g = p[root][i].first;
        if(g != fa && !vis[g]){
            findRoot(g,root);
            nodes[root] += nodes[g];
            dp[root] = max(dp[root],nodes[g]);
        }
    }
    dp[root] = max(dp[root],all - nodes[root]);
    if(dp[root] < num){
        num = dp[root];center = root;
    }
}
int getRoot(int root,int sn){
    num = INF;all = sn;center = root;
    findRoot(root,-1);
    return center;
}
void getDp(int root,int fa){
    nodes[root] = 1;
    depV.push_back(dep[root]);
    FI(p[root].size()){
        int g = p[root][i].first;
        if(g != fa && !vis[g]){
            dep[g] = dep[root] + p[root][i].second;
            getDp(g,root);
            nodes[root] += nodes[g];
        }
    }
}
ll cal(int root,bool isfirst){
    depV.clear();
    if(isfirst)
        dep[root] = 0;
    getDp(root,-1);
    sort(depV.begin(),depV.end());
    ll sum = 0;
    for(int i = 0,j = depV.size() - 1;i < depV.size();i++){
        while(j>i && depV[i] + depV[j] > k) j--;
        if(j > i)
            sum += (ll)(j - i);
    }
    return sum;
}
int work(int root,int fa){
    vis[root] = true;
    ans += cal(root,true);
    FI(p[root].size()){
        int g = p[root][i].first;
        if(g != fa && !vis[g]){
            ans -= cal(g,false);
            work(getRoot(g,nodes[g]),-1);
        }
    }
}
int main()
{
    while(S2(n,k)!=EOF && n + k)
    {
        ans = 0;
        FI(n) p[i + 1].clear(),vis[i + 1] = false;
        FI(n-1){
            S2(u,v);S(l);
            p[u].push_back(mp(v,l));
            p[v].push_back(mp(u,l));
        }
        work(getRoot(1,n),-1);
        printf("%lld\n",ans);
    }
    return 0;
}


Home Page   Go Back  To top

你可能感兴趣的:(poj1741 Tree 树的点分冶)