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

树上的点分治:分块的思想,只不过这个分块是很讲究的,每次我们都去找一个块的重心(子树中最大子树的结点树最少),这样的话,\log(n) 次就可以将整棵树都 拆完了。然后就是计数了,我们考虑所有合法的路径,对于每个树根,合法的路径,

为了速度,考虑直接计算两两相加小于K的所有组合,肯定是有不合法的,比如在同一课子树中选两个点,所以我们再减去所有不合法的,就是经过这个点的 <=2课子树的 情况了。

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)

const int N=10010;

struct Edge{
	int u,v,nt,w;
	Edge(int _u=0,int _v=0,int _nt=0,int _w=0){
		u=_u,v=_v,nt=_nt,w=_w;
	}
}edge[N*2];
int head[N],cnt;
void add_edge(int u,int v,int w){
	edge[cnt]=Edge(u,v,head[u],w);
	head[u]=cnt++;
}


bool vis[N];
int n,K,sz[N];
int max_num,rt,tot;//最大子树的大小,子树祖先,子树大小
void get_rt(int u,int f){
	sz[u]=1;int t=0;
	for(int i=head[u];i!=-1;i=edge[i].nt){
		Edge& e=edge[i];
		if(vis[e.v]||e.v==f)continue;
		get_rt(e.v,u);
		sz[u]+=sz[e.v];
		t=max(t,sz[e.v]);
	}
	t=max(t,tot-sz[u]);
	if(t<max_num){
		max_num=t,rt=u;
	}
}

int deep[N],ans[N];
int rec[N];
void get_deep(int u,int f){
	rec[++rec[0]]=deep[u];
	for(int i=head[u];i!=-1;i=edge[i].nt){
		Edge& e=edge[i];
		if(vis[e.v]||e.v==f)continue;
		deep[e.v]=deep[e.u]+e.w;
		get_deep(e.v,u);
	}
}

int solve(int u,int s){
	rec[0]=0;
	deep[u]=s;
	get_deep(u,0);
	sort(rec+1,rec+1+rec[0]);
	//printf("\nu:%d\n",u);
	//for(int i=1;i<=rec[0];i++)printf("i:%d rec:%d deep:%d\n",i,rec[i],deep[i]);
	int l,r; int ans=0;
	for(l=1,r=rec[0];l<r;++l){
		while(rec[l]+rec[r]>K)r--;
		if(l<=r){
			ans+=r-l;
		}
		//printf("l:%d r:%d ans:%d\n",l,r,ans);
	}
	//printf("ans:%d\n",ans);
	return ans;
}

void work(int u){
	vis[u]=1;
	ans[u]=solve(u,0);
	
	for(int i=head[u];i!=-1;i=edge[i].nt){
		Edge& e=edge[i];
		if(vis[e.v])continue;
		ans[u]-=solve(e.v,e.w);

		rt=e.v,max_num=sz[e.v],tot=sz[e.v];
		get_rt(e.v,rt);
		work(rt);
	}
}

int main(){
	while(scanf("%d %d",&n,&K)==2){
		if(!n&&!K)break;
		cnt=0;
		fill(head,head+n+1,-1);
		fill(vis,vis+n+1,0);

		rep(i,0,n-1){
			int u,v,w;
			scanf("%d %d %d",&u,&v,&w);
			add_edge(u,v,w);
			add_edge(v,u,w);
		}

		max_num=n,rt=1,tot=n;
		get_rt(1,0);
	//	printf("rt:%d\n",rt);
		work(rt);
		//rep(i,1,n+1)printf("i:%d deep:%d\n",i,deep[i]);
		int res=0;
		for(int i=1;i<=n;i++)res+=ans[i];
		printf("%d\n",res);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值