poj 1741 Tree(树的分治)

思路:转自别人的:

将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。

如果我们已经知道了此时所有点到根的距离a[i],a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算)。

在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)。

树的重心的算法可以线性求解。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn= 10000+5;
#define inf 1e9
vector<pair<int,int> >e[maxn];
vector<int>dep;
int n,k,res,ans,size,root;
int siz[maxn],d[maxn],vis[maxn],dp[maxn];
void dfs1(int u,int fa)         //求重心
{
	siz[u]=1;
	dp[u]=0;
	for(int i = 0;i<e[u].size();i++)
	{
		int v = e[u][i].first;
		if(v==fa || vis[v])
			continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		dp[u] = max(dp[u],siz[v]);
	}
	dp[u]=max(dp[u],size-siz[u]);
	if(dp[u] < dp[root])
		root = u;
}
void dfs2(int u,int fa)
{
	dep.push_back(d[u]);
	//siz[u]=1;
	for(int i = 0;i<e[u].size();i++)
	{
		int v = e[u][i].first;
		int w = e[u][i].second;
		if(v==fa || vis[v])
			continue;
		d[v]=d[u]+w;
		dfs2(v,u);
	//	siz[u]+=siz[v];
	}
}
int cal(int u,int di)
{
	dep.clear();
	d[u]=di;
	dfs2(u,-1);
	sort(dep.begin(),dep.end());
	int ress = 0;
	int l = 0,r=dep.size()-1;
	while(l<r)
	{
		if(dep[l]+dep[r]<=k)
			ress+=r-l++;
		else
			r--;
	}
	return ress;
}
void dfs(int u)
{
	ans+=cal(u,0);
	vis[u]=1;
	for(int i = 0;i<e[u].size();i++)
	{
		int v = e[u][i].first;
		if(vis[v])
			continue;
		ans-=cal(v,e[u][i].second);
        //dp[0] = 
		size = siz[v];
		root = 0;
		//res = 0;
		dfs1(v,-1);
		//printf("%d\n",root);
		dfs(root);
	}
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF && (n+k))
	{
		for(int i = 0;i<=n;i++)
			e[i].clear();
		memset(vis,0,sizeof(vis));
		for(int i = 1;i<n;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			e[u].push_back(make_pair(v,w));
			e[v].push_back(make_pair(u,w));
		}
		size = n;
		dp[0]=inf;
        dfs1(1,-1);
		ans = 0;
	//	printf("%d\n",root);
		dfs(root);
		printf("%d\n",ans);
	}
}



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



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值