poj 1741 Tree 树上的分治

http://poj.org/problem?id=1741

算法:

寻找树的重心;

分治;

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10010;
int n , m , k;
int size[maxn] , mx[maxn] , mi , dis[maxn] , center , cnt , ans;
bool vis[maxn];
struct Edge {
	int v , w , next;
	Edge() {}
	Edge(int v,int w,int next) : v(v) , w(w) , next(next) {};	
}edge[maxn<<1];
int E , head[maxn];
void init() {
	E = 0;
	memset(head,-1,sizeof(int)*(n+1));	
}
void addedge(int u,int v,int w) {
	edge[E] = Edge(v,w,head[u]); head[u] = E++;
	edge[E] = Edge(u,w,head[v]); head[v] = E++;	
}
void dfssize(int u,int fa) {
	size[u] = 1;
	mx[u] = 0;
	for(int i=head[u];i!=-1;i=edge[i].next) {
		int v = edge[i].v;
		if(vis[v] || v == fa) continue;
		dfssize(v , u);
		size[u] += size[v];
		if(size[v] > mx[u]) mx[u] = size[v];	
	}
	return;
}
void dfscenter(int r,int u,int fa) {
	int tmp = max(mx[u] , size[r]-size[u]);
	if(tmp < mi) mi = tmp , center = u;
	for(int i=head[u];i!=-1;i=edge[i].next) {
		int v = edge[i].v;
		if(vis[v] || v == fa) continue;
		dfscenter(r,v,u);	
	}	
	return;
}
void dfsdis(int u,int d,int fa) {
	dis[cnt++] = d;
	for(int i=head[u];i!=-1;i=edge[i].next) {
		int v = edge[i].v;
		if(vis[v] || v == fa) continue;
		dfsdis(v,d+edge[i].w,u);
	}
	return;
}
int calc(int u,int d) {
	int ret = 0;
	cnt = 0;
	dfsdis(u,d,-1);
	sort(dis,dis+cnt);
	int i = 0 , j = cnt - 1;
	while(i < j) {
		while(i<j & dis[i]+dis[j]>k) j--;
		ret += j-i;
		i++;
	}
	return ret;
}
void dfs(int u) {
	mi = n;
	dfssize(u,-1);
	dfscenter(u,u,-1);
	ans += calc(center,0);
	vis[center] = true;
	for(int i=head[center];i!=-1;i=edge[i].next) {
		int v = edge[i].v;
		if(vis[v]) continue;
		ans -= calc(v,edge[i].w);
		dfs(v);
	}
}
int main() {
	while(~scanf("%d%d" , &n,&k) && n+k) {
		init();
		memset(vis,false,sizeof(bool)*(n+1));
		ans = 0;
		for(int i=1;i<n;i++) {
			int u ,v , w;
			scanf("%d%d%d" ,&u,&v,&w);
			addedge(u,v,w);	
		}
		dfs(1);
		printf("%d\n" , ans);
	}
	return 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值