现在的位置: 首页 > 算法 > 正文

poj 1741 (树的分治)

2018年12月27日 算法 ⁄ 共 1942字 ⁄ 字号 评论关闭

题意:给定一棵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;
}

抱歉!评论已关闭.