zoj 3195(LCA)


题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3320


题意描述:给你含有n个点(n<=50000)无向无环图,给出Q个查询,每个查询有三个点,问这三个点组成的最近距离为多少


分析:这三个的最近距离等于每两个点的最近距离之和除以2,因为查询太多,我们可以采用LCA离线算法来求出需要的两个点之间的距离!!

总结:离线的LCA是先将所有的查询先存放起来,然后在计算每一个的结果,但是输出时要将输入和结果一一对应起来,这个采用邻接表存放的话,输入和边就有着一一对应的关系,所以可以根据边来顺序输出结果


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int M = 50010;
struct node
{
	int x,y,nxt,w;
}edge[M*2];
int head[M],e;
void addedge(int x, int y,int w)
{
	edge[e].x=x;
	edge[e].y=y;
	edge[e].w=w;
	edge[e].nxt=head[x];
	head[x]=e++;
}
struct node1
{
	int x,y,d,nxt;
}edge1[M*20];
int head1[M],e1;

void add(int x, int y)
{
	edge1[e1].x=x;
	edge1[e1].y=y;
	edge1[e1].d=0;
	edge1[e1].nxt=head1[x];
	head1[x]=e1++;
}
int father[M];
int find(int x)
{
	while(x!=father[x])
		x=father[x];
	return x;
}
int d[M];
bool flag[M],vis[M];
void tarjan_lca(int u)
{
	int i,v;
	vis[u]=true;
	father[u]=u;
	for(i=head[u];i!=-1;i=edge[i].nxt)
	{
		v=edge[i].y;
		if(!vis[v])
		{
	   	    d[v]=d[u]+edge[i].w;
	    	tarjan_lca(v);
	    	father[find(v)]=u;//找到u的子树v后将其祖先置为u
		}
	}
	flag[u]=true;
	for(i=head1[u];i!=-1;i=edge1[i].nxt)
	{
		v=edge1[i].y;
		if(flag[v])
			edge1[i].d=d[u]+d[v]-2*d[find(v)];
	}
}
			
int main ()
{
	int n;
	int i,x,y,w,j,m,z;
	int k=0;
	while(scanf("%d",&n)!=EOF)
	{
		memset(head,-1,sizeof(head));
		e=0;	
		for(i=1;i<n;i++)//建立树
		{
			scanf("%d%d%d",&x,&y,&w);
			addedge(x+1,y+1,w);
			addedge(y+1,x+1,w);
		}
        scanf("%d",&m);
		memset(head1,-1,sizeof(head1));
		e1=0;
		for(j=0;j<m;j++) //建立查询树
		{
			scanf("%d%d%d",&x,&y,&z);
			x++;y++;z++;
			add(x,y);   
			add(y,x);
			add(x,z);
			add(z,x);
			add(y,z);
			add(z,y);
		}
		memset(flag,0,sizeof(flag));
		memset(vis,0,sizeof(vis));
		d[1]=0;
	//	for(i=1;i<=n;i++)
		//	father[i]=i;
		tarjan_lca(1);//将节点1看成为根节点
		if(k>0)printf("\n");
		k++;
		for(i=0;i<e1;i+=6)
		{
			int num=0;
			if(edge1[i].d!=0)
				num+=edge1[i].d;
			else num+=edge1[i+1].d;
			if(edge1[i+2].d!=0)
			   num+=edge1[i+2].d;
			else num+=edge1[i+3].d;
			if(edge1[i+4].d!=0)
				num+=edge1[i+4].d;
			else num+=edge1[i+5].d;
			printf("%d\n",num/2);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值