点分治 poj1741

楼教主的男人八题之一。。。。

狂T了一天,最后以把x错打成i而结束了漫长的debug。。。。


这个题是我的第一个树分治


树分治其实就是分治思想在树上的应用,在这个题中具体点就是把一颗子树中的路径分别计算然后递归到子树中再经行下一步的计算直到子树只有一个点


这个操作是基于点的所以叫点分治


这个题要有一个基础就是poj1655求树的重心

如果不会看这里


这样的话我们从一开始的大树中选出重心,然后计算对ans的贡献,然后递归到好多个子树当中去就可以了


特别要注意的是,本题还有了一个双指针手法,具体的可以看代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAX 20009
#define rep(i,j,k) for(int i=j;i<=k;i++)

using namespace std;

int tot,to[MAX],value[MAX],head[MAX],next[MAX],q,u[MAX],n,k;
int done[MAX],size[MAX],num[MAX];
int t,ans=0;

struct wbysr
{
	int dis,belong;
}a[MAX];

bool cmp(wbysr a1,wbysr a2)
{
	return a1.dis<a2.dis;
}

void add(int from,int To,int weight)
{
	tot++;
	to[tot]=To;
	value[tot]=weight;
	next[tot]=head[from];
	head[from]=tot;
}

void dfs(int x,int fa)
{
	u[++q]=x;
	size[x]=1;
	num[x]=0;
	for(int i=head[x];i!=-1;i=next[i])
		if(!done[to[i]]&&to[i]!=fa)
			dfs(to[i],x),size[x]+=size[to[i]],num[x]=max(num[x],size[to[i]]);
	return;
}

int find(int x)
{
	q=0;
	dfs(x,0);
	int Return=x,Min=0x7fffffff;
	rep(i,1,q)
		if(max(size[x]-size[u[i]],num[u[i]])<Min)
			Min=max(size[x]-size[u[i]],num[u[i]]),Return=u[i];
	return Return;
}

void dfs2(int x,int fa,int dist,int Belong)
{
	t++;
	a[t].belong=Belong;
	a[t].dis=dist;
	for(int i=head[x];i!=-1;i=next[i])
		if(to[i]!=fa&&!done[to[i]])
			dfs2(to[i],x,dist+value[i],Belong);
}

void calc(int x)
{
	t=0;
	a[++t].belong=x;
	a[t].dis=0;
	for(int i=head[x];i!=-1;i=next[i])
		if(!done[to[i]])
			dfs2(to[i],x,value[i],to[i]);
	sort(a+1,a+1+t,cmp);
	int r=t,same[10009]={0};
	rep(i,1,t)
		same[a[i].belong]++;
	for(int l=1;l<=t;l++)
	{
		while(a[l].dis+a[r].dis>k&&r>l)
			same[a[r].belong]--,r--;
		same[a[l].belong]--;
		if(r>l)
			ans+=r-l-same[a[l].belong];
	}
}

inline void work(int x)
{
	int root=find(x);
	done[root]=1;
	calc(root);
	for(int i=head[root];i!=-1;i=next[i])
		if(!done[to[i]])
			work(to[i]);
	return;
}

int main()
{
	while(scanf("%d%d",&n,&k)&&n+k)
	{
		memset(done,0,sizeof(done));
		memset(head,-1,sizeof(head));
		ans=tot=0;
		for(int i=1,a1,a2,a3;i<=n-1;i++)
			scanf("%d%d%d",&a1,&a2,&a3),add(a1,a2,a3),add(a2,a1,a3);
		work(1);
		printf("%d\n",ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值