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