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的组数
思路:
比较综合的一道树上点分治,虽说分解问题后每一步都比较好想到,但是写起来还是没那么轻松。。
一. 找出当前树重心作为根
写的树上点分治不多,但是找重心是维护一个递归深度的常用方法,比较直接的做法就是从重心的定义(去掉重心,各个新生成的树的结点数 不超过 原来树的总结点数的一半)出发,找一个点判断去掉它之后是否满足要求,如果满足它就是重心了,一遍dfs可以解决,复杂度为 N
二. 求出所有点的到根距离
这个比较容易,还是一遍dfs解决,复杂度为 N
三. 统计组数
分两个小部分
1.统计过根的组数
这个我的做法是在将(二)中所有点到根的距离存进数组,进行排序(NlogN),然后用双指针(N)就能得到,写的不是很熟练...
需要注意是因为过根,所以必然是不同子树,那么需要记录每个点属于哪个子树,统计 (总数目-同一子树下的数目)即可。
2.统计到根的组数
这个在(二)中进行dfs时直接统计就好
四. 递归子树
然后把根移出,递归它的子树即可
那么总复杂度就为 NlogNlogN,满足题目的要求
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
/**
1. 找出重心作为根
2. 求出所有点的到根距离
3. 统计组数
4. 递归子树
**/
struct edge{
int t;
int w;
edge(){}
edge(int tt,int ww){
t = tt ; w = ww;
}
};
int cnt,n,m ;
bool root[10005];
vector<edge>G[10005];
int has[10005];
int len[100005];
int F[100005];
int ID[10005];
int had[10005];
inline int get(int x,int f,const int& all,int &R){
had[x]=0;
has[x] = 1;
int mx = 0;
for(int i=0;i<G[x].size();i++){
edge& j = G[x][i];
if(!root[j.t]&&j.t!=f){
has[x]+=get(j.t,x,all,R);
mx = max(mx,has[j.t]);
}
}mx = max(mx,all-has[x]);
if(mx<=(all>>1)){
R = x;
}return has[x];
}
inline void oper(int x,int f,const int& ff,int d,int &all){
if(d>m)return;
F[cnt]=ff;
len[cnt]=d;
ID[cnt]=cnt++;
if(d<=m)all++;
for(int i=0;i<G[x].size();i++){
edge &j = G[x][i];
if(!root[j.t]&&j.t!=f){
oper(j.t,x,ff,d+j.w,all);
}
}
}
inline bool cmp(const int& x,const int& y){
return len[x]<len[y];
}
inline int get_num(){
int id = 0;
sort(ID,ID+cnt,cmp);
int res = 0;
for(int i=cnt-1;i>0;i--){
int &ii = ID[i];
while(id<i&&len[ID[id]]+len[ii]<=m){
had[F[ID[id++]]]++;
}while(id>i){
had[F[ID[--id]]]--;
}
res += id - had[F[ii]];
}return res;
}
inline int work(int x,int num){
int res = 0;
int R;
get(x,x,num,R);
root[R]=true;
cnt = 0;
for(int i=0;i<G[R].size();i++){
edge &j = G[R][i];
if(!root[j.t]){
oper( j.t , R , j.t , j.w , res );
}
}
res += get_num();
for(int i=0;i<G[R].size();i++){
edge &j = G[R][i];
if(!root[j.t]){
res += work(j.t,has[j.t]<=(num>>1)?has[j.t]:num-has[R]);
}
}
return res;
}
int main()
{
while(scanf("%d%d",&n,&m)==2&&(n||m)){
for(int i=0;i<10005;i++){
G[i].clear();
had[i]=has[i]=root[i]=false;
}
for(int i=1,s,t,w;i<n;i++){
scanf("%d%d%d",&s,&t,&w);
G[s].push_back(edge(t,w));
G[t].push_back(edge(s,w));
}printf("%d\n",work(1,n));
}return 0;
}
/*
13 6
1 2 1
1 3 1
1 4 1
2 5 1
2 6 1
2 7 1
4 8 1
4 9 1
4 10 1
3 11 1
3 12 1
3 13 1
78
*/