CS20 D LCA

给出一棵树,许多询问,每次询问A,B,C三点,求一点使到三点距离最小,输出该点和最小值。

很明显就是求LCA,三种组合都求一次LCA,然后在里面选个距离和最小的就行了。

官方题解里面的代码求LCA是在线DFS RMQ的方法..先记录欧拉序,且记录某个点在序列里的第一个位置,每次询问a,b的LCA就是询问两者在欧拉序列里第一个位置之差中的那些点里面深度最小的

LCA(a,b)=RMQ(dep, pos[a], pos[b])

/** @Date    : 2017-09-27 20:41:28
  * @FileName: CS20 C LCA RMQ.cpp
  * @Platform: Windows
  * @Author  : Lweleth ([email protected])
  * @Link    : https://github.com/
  * @Version : $Id$
  */
#include <bits/stdc++.h>
#define LL long long
#define PII pair<int ,int>
#define MP(x, y) make_pair((x),(y))
#define fi first
#define se second
#define PB(x) push_back((x))
#define MMG(x) memset((x), -1,sizeof(x))
#define MMF(x) memset((x),0,sizeof(x))
#define MMI(x) memset((x), INF, sizeof(x))
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5+20;
const double eps = 1e-8;

int dep[N];
int pos[N];
int rmq[19][2*N];
int eul[2*N], c, l[2*N];

vector<int> edg[N];

void dfs(int x, int pre)
{
	eul[++c] = x;
	pos[x] = c;
	if(pre) dep[x] = dep[pre] + 1;
	for(auto i: edg[x])
	{
		if(i == pre)
			continue;
		dfs(i, x);
		eul[++c] = x;
	}
}

void init()
{
	dfs(1, 0);
	for(int i = 2; i <= c; i++)//预处理 2^k=x对应的k
		l[i] = l[i / 2] + 1;
	for(int i = 1; i <= c; i++)
		rmq[0][i] = eul[i];
	for(int j = 1; (1 << j) <= c; j++)
		for(int i = 1; i <= c; i++)
		{
			rmq[j][i] = rmq[j - 1][i];
			if(i + (1 << (j - 1)) > c)
				continue;
			if(dep[rmq[j - 1][i + (1 << (j - 1))]] < dep[rmq[j][i]])
				rmq[j][i] = rmq[j - 1][i + (1 << (j - 1))];
		}
}

int lca(int x, int y)
{
	if(pos[x] > pos[y])
		swap(x, y);
	int dis = pos[y] - pos[x] + 1;
	int k = l[dis];
	if(dep[rmq[k][pos[x] + dis - (1 << k)]]
		< dep[rmq[k][pos[x]]])
		return rmq[k][pos[x] + dis - (1 << k)];
	else return rmq[k][pos[x]];
}

int distance(int a, int b)
{
	int ac = lca(a, b);
	return dep[a] + dep[b] - 2 * dep[ac];
}
int main()
{
	int n, q;
	cin >> n >> q;
	for(int i = 0; i < n - 1; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		edg[x].PB(y);
		edg[y].PB(x);
	}
	init();
	while(q--)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		int ac1 = lca(a, b);
		int ac2 = lca(a, c);
		int ac3 = lca(b, c);
		int ans1 = distance(ac1, a) + distance(ac1, b) + distance(ac1, c);
		int ans2 = distance(ac2, a) + distance(ac2, b) + distance(ac2, c);
		int ans3 = distance(ac3, a) + distance(ac3, b) + distance(ac3, c);
		if(ans1 > ans2)
			swap(ans1, ans2), swap(ac1, ac2);
		if(ans1 > ans3)
			swap(ans1, ans3), swap(ac1, ac3);
		printf("%d %d\n", ac1, ans1);
	}
    return 0;
}
时间: 2024-08-26 10:05:23

CS20 D LCA的相关文章

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

SZOJ 167 Lca裸题

一道.......一道我改了一周的裸题 无根树建双向边 无根树建双向边 无根树建双向边 重要的事情说三遍(微笑) 还有要开longlong 还有双向边不是双倍边(微笑) 我真是,能把自己气吐血10次就不把自己气吐血9次 [问题描述] 已知一棵nn个点的树,点从1开始标号,树上每条边都有一个正整数边权. 有qq个询问,每个询问由type,u,vtype,u,v三个正整数构成. 当type=1type=1时,询问uu到vv路径上所有边权的二进制异或和. 当type=2type=2时,询问uu到vv路

【LCA/tarjan】POJ1470-Closest Common Ancestors

[题意] 给出一棵树和多组查询,求以每个节点为LCA的查询数有多少? [错误点] ①读入的时候,注意它的空格是随意的呀!一开始不知道怎么弄,后来看了DISCUSS区大神的话: 询问部分输入: scanf("%d",&m); for(int i=0;i<m;i++){ scanf(" (%d %d)",&a,&b); } 注意scanf(" 这里有一个空格 ②多组数据啊!注意这句话:The input file contents

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然

hdu3078 建层次树+在线LCA算法+排序

题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点的父亲节点).在对每次询问,走一遍一次公共祖先路上 的权,保持,快排.n*logn*q #include<iostream> //187MS #include<algorithm> #include<cstdio> #include<vector> using

HDU 6203 ping ping ping(dfs序+LCA+树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=6203 题意: n+1 个点 n 条边的树(点标号 0 ~ n),有若干个点无法通行,导致 p 组 U V 无法连通.问无法通行的点最少有多少个. 思路: 贪心思维,破坏两个点的LCA是最佳的.那么怎么判断现在在(u,v)之间的路径上有没有被破坏的点呢,如果没有的话那么此时就要破坏这个lca点.一开始我们要把询问按照u和v的lca深度从大到小排序,如果某个点需要被破坏,那么它的所有子节点都可以不再需要破坏别的点

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

【最近公共祖先Tarjan】Tarjan求LCA练习

Tarjan求LCA 这是一篇非常好的讲解,靠这个文章搞懂的~ 1 void tarjan(int u) 2 { 3 vis[u]=1; 4 for(int i=0;i<edge[u].size();i++) 5 { 6 int v=edge[u][i]; 7 if(vis[v] == 0) 8 { 9 tarjan(v); 10 p[v]=u; 11 } 12 } 13 for(int i=0;i<qy[u].size();i++) 14 { 15 int v=qy[u][i].v,id=q

hdu3830(lca + 二分)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3830 题意: 有三个点 a, b, c, 对于其中任意一点 x 可以跨过一个点移动到另一个位置, 当且仅当移动前后的 x 与其所跨越的点的距离相等 .给出两组点, 问其能否相互到达, 若能并输出最少需要移动多少步 . 思路: http://www.cnblogs.com/scau20110726/archive/2013/06/14/3135024.html 代码: 1 #include <ios