luogu P4281 [AHOI2008]紧急集合 / 聚会 |LCA

题目描述

欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有 n 个等待点,有 n?1 条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。

参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在 n 个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。

小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?

输入格式

第一行两个正整数 n 和 m,分别表示等待点的个数(等待点也从 1 到 n 进行编号)和获奖所需要完成集合的次数。

随后 n?1 行,每行两个正整数 a,b,表示编号为 a 和编号为 b 的等待点之间有一条路。

随后 m 行,每行用三个正整数 x,y,z,表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。

输出格式

输出共 m 行,每行两个用空格隔开的整数 p,c。其中第 i 行表示第 i 次集合点选择在编号为 p 的等待点,集合总共的花费是 c 个游戏币。



答案必然在某两个的LCA上

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e5+10;
int nxt[N<<1],head[N],go[N<<1],tot;
inline int read(){
	int x=0; char c=getchar();
	while(c<‘0‘||c>‘9‘)c=getchar();
	while(‘0‘<=c&&c<=‘9‘){ x=(x<<1)+(x<<3)+c-‘0‘; c=getchar(); }
	return x;
}
inline void add(int u,int v){
	nxt[++tot]=head[u],head[u]=tot,go[tot]=v;
	nxt[++tot]=head[v],head[v]=tot,go[tot]=u;
}
int f[N][20],dep[N];
void Deal(int u,int fa){
	f[u][0]=fa,dep[u]=dep[fa]+1;
	for(int i=1;i<=19;i++)f[u][i]=f[f[u][i-1]][i-1];

	for(int i=head[u];i;i=nxt[i]){
		int v=go[i];
		if(v==fa)continue;
		Deal(v,u);
	}

}
inline int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=19;i>=0;i--)
	if(f[x][i]!=f[y][i]){
		x=f[x][i];
		y=f[y][i];
	}
	return f[x][0];
}
int n,m;
inline int dis(int x,int y){
	return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<n;i++)add(read(),read());

	Deal(1,1);
	int x,y,z;
	while(m--){
		x=read(),y=read(),z=read();
		int ans=1e9,w,mzx;
		int a1=LCA(x,y),a2=LCA(x,z),a3=LCA(z,y);

		mzx=dep[x]+dep[y]-2*dep[a1]+dis(z,a1);
		if(ans>mzx)ans=mzx,w=a1;
		mzx=dep[x]+dep[z]-2*dep[a2]+dis(y,a2);
		if(ans>mzx)ans=mzx,w=a2;
		mzx=dep[z]+dep[y]-2*dep[a3]+dis(x,a3);
		if(ans>mzx)ans=mzx,w=a3;

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

原文地址:https://www.cnblogs.com/naruto-mzx/p/12665861.html

时间: 2024-07-31 16:52:38

luogu P4281 [AHOI2008]紧急集合 / 聚会 |LCA的相关文章

[AHOI2008]紧急集合 / 聚会

紧急集合 / 聚会 题目大意: 给出一个无向图,每一次给出图中的三个点,求离三个点距离之和最小的点. 解决方法: 倍增LCA. 首先我们两两点之间求出LCA,那么离他们距离之和最近的点就是三个点中深度最深的点,想一想为什么? 我们假设存在一个点离三个点距离之和更近且深度更浅,那么我们将它的深度往下走一个,一定会有两个点距离-1,一个点+1,所以往下移更靠近正解opt,那么我们就可以得出我们上述结论合法. 而关于距离之和,由于dis(u,v)=dep[u]+dep[v]-dep[lca(u,v)]

BZOJ 1787 AHOI2008 紧急集合 倍增LCA

题目大意:给定一棵树,多次询问到三个点距离之和最小的点和距离 首先易知到两个点距离之和最小的点一定在两点间的路径上 于是到三个点距离之和最小的点一定在两两之间路径的交点上 然后很容易就会知道这个交点一定是其中两个点的LCA(其实是我不会证) 此外为什么不会是三个点共同的LCA呢?因为三个点共同的LCA一定是至少一对点的LCA 证明略(其实我也不会证) 然后就是枚举两两之间的LCA 求一下距离 取最小即可 然后就是倍增LCA的问题了 我的倍增LCA怎么又挂了 还能不能写对了0.0 #include

luoguP4281[AHOI2008]紧急集合 / 聚会

最近复习lca,布置的三道题中最后一道就是这个紫题.当时看到是紫题吓了一跳(果然是我太弱了QAQ)然后仔细读了几遍题,发现这就是普通的lca嘛 (太水了) 其中变化的只有要分别求三者的lca. #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef long long ll; struct kh{ int t,

「AHOI2008」「LuoguP4281」紧急集合 / 聚会(LCA

题目描述 欢乐岛上有个非常好玩的游戏,叫做“紧急集合”.在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币. 参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费).地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系).当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所

AHOI2008 紧急集合 树上倍增

AHOI2008 紧急集合 题目传送 sol: 如果只有两个点,那么显然目的地就是在他们二者路径上的任意一点. 现在有三个点,考虑两两的路径和lca,发现肯定有两对求得的lca相同,另外一对的lca深度比那两对的lca深度大. 这个深度大一些的那个lca就是目的地(最近点),最小距离就是三者两两距离的二分之一. 所以直接树上倍增即可. #include<bits/stdc++.h> #define IL inline #define RG register #define DB double

【Ahoi2008】【lca】【bzoj1787】Meet 紧急集合

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1787 题解: 求出三个点两两之间的lca会发现有两个是一样的,然后我们选那个不一样的就好了. #include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; struct use{ int st,en; }b[5000001]; in

[bzoj1787][Ahoi2008]Meet 紧急集合(lca)

传送门 可以看出,三个点两两之间的lca会有一对相同,而另一个lca就是聚集点. 然后搞搞就可以求出距离了. ——代码 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define MAXN 1000001 5 6 using namespace std; 7 8 int n, m, cnt, ans; 9 int head[MAXN], to[MAXN], next[MAXN], de

【bzoj1787】[Ahoi2008]Meet 紧急集合 倍增LCA

题目描述 输入 输出 样例输入 6 4 1 2 2 3 2 4 4 5 5 6 4 5 6 6 3 1 2 4 4 6 6 6 样例输出 5 2 2 5 4 1 6 0 题解 倍增LCA 首先有集合点必定在三点中两个点的LCA处,大概画一下就看出来了. 然后有x到y的距离为deep[x]+deep[y]-2*deep[lcaxy] 那么x.y.z三点到lcaxy的距离为deep[x]+deep[y]-2*deep[lcaxy]+deep[lcaxy]+deep[x]-deep[lcaxyz] 到

【BZOJ1832】【AHOI2008】聚会 倍增lca

这道题写不了tarjanlca. 50W的询问,也就是150W次lca查询,每次加三条边,内存妥妥要爆. 只能退求logn的倍增lca了. sad story. 还好一遍AC. 题解: 就是发现每次询问三个点之间有唯一的路径集,那么我们选其中两个取lca,然后另一个点自己走到这个lca, 就可以贪心取得答案. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorit