lca

//目前会三个版本
//1.倍增法。看挑战。
//2.rmq.看挑战
//3.离线 tarjan看http://www.cnblogs.com/JVxie/p/4854719.html
//离线的基本原理是依据dfs的括号性的(看算法导论找dfs括号性)
//在已知各个询问的时候我们阔以用并查集来辅助。。首先init()  另father[i]=i;然后开始dfs()
//这里还需要一个visit[i]数组,用以记录以i为根的子树是否已经遍历完成。如果有询问(a,b)即询问a与b的公共祖先。那么在dfs时,如果以a为根的树
//遍历完成时此时,刚刚要把visit[a]=1在此之前我们把father[a]修改为dfs时a的父亲节点然后看看visit[b]是否等于1如果visit[b]=1
//那么find(a)函数返回的值就是最近公共祖先(什么是find(a)函数,看挑战程序设计竞赛并查集一张即知)如果visit[b]=0那么等到visit[b]要成为1时
//再对b做相似的操作。
//举例:点1连点2 点1连点3 1为根  询问(2,3)init()然后dfs(1)开始 到点2发现点2没有子节点了此时以2为根的子树遍历完成,准备把visit[2]=1,
//在这之前father[2]=1;然后看visit[3]==1,现在visit[3]=0所以什么都不做然后visit[2]=1,然后回到1.此时1还没遍历完,开始遍历3,发现以3为根的
//子树无子节点。遍历完成,准备visit[3]=1在这之前修改father[3]=1,然后看visit[2]==1(为什么知道是看2点呢?因为询问的是(2,3)嘛。
//如果还有询问(3,4)当然还要看visit[4])此时,visit[2]=1所以find(3);函数返回值1,所以询问(2,3)的值为1.其他都一样。。。。很简单嘛。。
//poj 1470(tarjan)  

bzoj 1787
//有坑点。。也许算不上。。主要是我太弱。。。。
//此题如果用深度的绝对值来求距离就会造成错误 比如输入以下数据:
//6 1
//1 2
//2 5
//2 6
//6 3
//6 4
//5 3 4
//正确答案是6 4 然而用绝对值就是6 2就呵呵了
//此题除了上述我犯错外还有标记(1)那个地方写错了i>=0而不是i>0 还有就是把distfa[num][i]写成了distfa[i][num];
//上述3个sb错误让我搞了一下午......
//此题的分析就是注意如果a,b,c中先求(a,b)的lca q和(b,c)的lca p然后如果dist[q]>=dist[q]证明a,b,都能到q 运用这种性质阔以分析出(a,c)的lca要么是p,要么是q或者是比q还深的点分类讨论一下就有网上说的结果了。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct edgee
{
	int to;
};
edgee edge[2000050];
int first[1000050], nextt[2000050];
int distfa[1000050][22], dist[1000050];;
int edgetot = 1,m,n;
void addedgee(int from, int to)
{
	edge[edgetot].to = to;
	nextt[edgetot] = first[from];
	first[from] = edgetot;
	edgetot++;
	edge[edgetot].to = from;
	nextt[edgetot] = first[to];
	first[to] = edgetot;
	edgetot++;
}
void dfs(int num,int fa,int deep)
{
	dist[num] = deep; distfa[num][0] = fa;
	for (int i = 1; distfa[num][i - 1]; i++){
		int a = distfa[distfa[num][i - 1]][i - 1]; int b = distfa[num][i - 1];
		distfa[num][i] = distfa[distfa[num][i-1]][i - 1];
	}
	for (int i = first[num]; i; i = nextt[i])
	{
		int to = edge[i].to;
		if (!dist[to])
		dfs(to, num, deep + 1);
	}
}
int lca(int a, int b)
{
	if (dist[a] > dist[b])swap(a, b);
	int len = dist[b] - dist[a];
	for (int i = 0; len; len >>= 1, i++)
	{
		int kind = len % 2;
		if (kind == 1)
			b = distfa[b][i]; 
	}
	int temp = 0;
	for (int i = 21; i>=0; i--)//(1)
	{
		if (distfa[a][i] != distfa[b][i])
			a = distfa[a][i], b = distfa[b][i];
	}
	if (a == b)
		temp = a;
	else
	    temp = distfa[a][0];
	return temp;
}
int getlenth(int a, int b)
{
	int r = lca(a, b);
	int k = dist[a] + dist[b] - 2 * dist[r];
	return k;
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		addedgee(a, b);
	}
	dfs(1, 0, 1);

	for (int i = 0; i < m; i++)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		int k1 = lca(a, b); int k2 = lca(b, c); int k3 = lca(a, c);
		int len,k;
			if (k1 == k2)
				len = getlenth(a,k3) + getlenth(b , k3) + getlenth(c, k3), k = k3;
			else
			{
				if (k2 == k3)
					len = getlenth(a, k1) + getlenth(b, k1) + getlenth(c, k1), k = k1;
				else
					if (k1 == k3)
						len = getlenth(a, k2) + getlenth(b, k2) + getlenth(c, k2), k = k2;
			}
		printf("%d %d\n", k, len);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值