poj 1741 (树的分治)

题意:给定一棵N(1<= N <=10000)个结点的带权树,定义dist(u,v)为u,v两点间的最短路径长度,路径的长度定义为路径上所有边的权和。再给定一个 K  ,如果对于不同的两个结点a,b,如果满足dist(a,b) <=K,则称(a,b)为合法点对。求合法点对个数。

思路:看了论文《分治算法在树的路径问题中的应用》,里面讲解的很清楚,一条路径要么过根节点,要么在一颗子树中,所以用分治算法。找到树的重心作为根节点,这样每次树的节点数至少减少一半。处理经过当前根节点路径<=k的点对数,然后把根节点去掉后就把原来的树分成几颗子树了,再处理子树。我们在求经过一个根节点的路径时,里面还包含了点对属于同一颗子树的情况,所以要去掉这部分的点。

dis(i)+dis(j)<=k(i,j的父节点不为根节点的同一个儿子)

=dis(i)+dis(j)<=k-dis(i)+dis(j)<=k(i,j的父节点属于根节点的同一儿子).





 

#include <algorithm>

#include<stdio.h>

#include<string.h>

const int N=10010;

using namespace std;

int head[N],num,f[N],son[N],n,D,root,size,ans,dis[N],d[N],cum;

bool vis[N];

#define max(a,b) (a<b?b:a)

struct edge

{

	int st,ed,w,next;

}e[N*2];

void addedge(int x,int y,int w)

{

	e[num].st=x;e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;

	e[num].st=y;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;

}

void getroot(int u,int father)//求树的重心

{

    int i,v;

	f[u]=0;son[u]=1;

	for(i=head[u];i!=-1;i=e[i].next)

	{

		v=e[i].ed;

		if(vis[v]||v==father)continue;

		getroot(v,u);

		son[u]+=son[v];

		f[u]=max(f[u],son[v]);

	}

	f[u]=max(f[u],size-son[u]);

	if(f[u]<f[root])root=u;

}

void getdis(int u,int father)//求节点到根节点的距离

{

	int i,v;

	son[u]=1;//更新子树的节点的子节点数,不更新也能ac

	d[cum++]=dis[u];//将点到根节点的距离加入数组

	for(i=head[u];i!=-1;i=e[i].next)

	{

		v=e[i].ed;

		if(vis[v]||v==father)continue;

		dis[v]=dis[u]+e[i].w;

		getdis(v,u);

		son[u]+=son[v];

	}

}

int cont(int u,int mit)

{

	int res=0,L,R;

	dis[u]=mit;

	cum=0;

	getdis(u,0);

	sort(d,d+cum);//将点到根节点的距离排序

	for(L=0,R=cum-1;L<R;)

	{

		if(d[L]+d[R]<=D)//如果d[L]+d[R]<=D,L代表的节点可以与(R-L)个节点成对

			res+=(R-L++);

		else R--;

	}

	return res;

}

void work(int u)

{

	int i,v;

   vis[u]=true;

   ans+=cont(u,0);//路径经过该根节点的点对数

   for(i=head[u];i!=-1;i=e[i].next)

   {

      v=e[i].ed;

	  if(vis[v])continue;

	  ans-=cont(v,e[i].w);//减去属于v子树的点对数

	  root=0;f[root]=size=son[v];

	  getroot(v,0);//求v子树的根节点

	  work(root);//求v子树的点对

   }

}

int main()

{

	int i,x,y,w;

	while(scanf("%d%d",&n,&D),n||D)

	{

		memset(head,-1,sizeof(head));

		num=0;

		for(i=1;i<n;i++)

		{

			scanf("%d%d%d",&x,&y,&w);

			addedge(x,y,w);

		}

		memset(vis,false,sizeof(vis));

		root=0;f[root]=size=n;ans=0;

		getroot(1,0);

		work(root);

		printf("%d\n",ans);

	}

	return 0;

}


 

 

你可能感兴趣的:(poj)