[AHOI2008] 紧急集合 / 聚会
题目描述
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有 n n n 个等待点,有 n − 1 n-1 n−1 条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。
参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在 n n n 个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。
小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
输入格式
第一行两个正整数 n n n 和 m m m,分别表示等待点的个数(等待点也从 1 1 1 到 n n n 进行编号)和获奖所需要完成集合的次数。
随后 n − 1 n-1 n−1 行,每行两个正整数 a , b a,b a,b,表示编号为 a a a 和编号为 b b b 的等待点之间有一条路。
随后 m m m 行,每行用三个正整数 x , y , z x,y,z x,y,z,表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。
输出格式
输出共 m m m 行,每行两个用空格隔开的整数 p , c p,c p,c。其中第 i i i 行表示第 i i i 次集合点选择在编号为 p p p 的等待点,集合总共的花费是 c c c 个游戏币。
样例 #1
样例输入 #1
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
样例输出 #1
5 2
2 5
4 1
6 0
提示
对于 40 % 40\% 40% 的数据, n ≤ 2 × 1 0 3 n\leq2\times10^3 n≤2×103, m ≤ 2 × 1 0 3 m\leq2\times 10^3 m≤2×103。
对于 100 % 100\% 100% 的数据, 1 ≤ x , y , z ≤ n ≤ 5 × 1 0 5 1\leq x,y,z\leq n\leq 5\times10^5 1≤x,y,z≤n≤5×105, 1 ≤ m ≤ 5 × 1 0 5 1\leq m\leq 5\times 10^5 1≤m≤5×105。
大致思路
要求出三个点走到同一个点所要求的最小花费,分析后可以得出这个公共的LCA点较其他点优。
求三个点两两之间的LCA,然后这三个LCA中有二者重合的点的另一点即为最优LCA点
QUESTION 1
它存在三种情况:最后三者所走到的最优公共点只可能为这二者之一。
假设我们当前有
X
Y
Z
XYZ
XYZ三个点,那么就会有
L
C
A
X
Y
LCAXY
LCAXY,
L
C
A
X
Z
LCAXZ
LCAXZ,
L
C
A
Y
Z
LCAYZ
LCAYZ,若其中一组相等,则另一个剩余的为最优点
例:
L
C
A
X
Y
=
=
L
C
A
Y
Z
LCAXY==LCAYZ
LCAXY==LCAYZ,则最后输出的最优点为
L
C
A
X
Z
LCAXZ
LCAXZ
QUESTION 2
对于深度(由于边权为1,所以深度
=
=
=花费),我们可以用
X
,
Y
,
Z
X ,Y ,Z
X,Y,Z及其
L
C
A
LCA
LCA的深度计算
已知三个点和其LCA的深度,那么LCA相等的两个点走到LCA后,再一起移动到与另一点的LCA的花费是两倍于另一点走到这一个相等的LCA的(两个人走一条路与一个人走一条路的区别,肯定是一个人走花费少)
因此我们可以得出:花费一定与三个点分别的LCA最小值和最大值有关
因为LCA实质上是在树上向上走,所以花费等于三个点深度和减去两倍的LCA最小值,减去一倍的LCA最大值
=
=
=
>
===>
===>
c
o
s
t
=
d
e
p
[
x
]
+
d
e
p
[
y
]
+
d
e
p
[
z
]
−
m
i
n
(
d
e
p
[
L
C
A
x
y
]
,
d
e
p
[
L
C
A
x
z
]
,
d
e
p
[
L
C
A
y
z
]
)
∗
2
−
m
a
x
(
d
e
p
[
L
C
A
x
y
]
,
d
e
p
[
L
C
A
x
z
]
,
d
e
p
[
L
C
A
y
z
]
)
cost=dep[x]+dep[y]+dep[z]-min(dep[LCAxy],dep[LCAxz],dep[LCAyz])*2-max(dep[LCAxy],dep[LCAxz],dep[LCAyz])
cost=dep[x]+dep[y]+dep[z]−min(dep[LCAxy],dep[LCAxz],dep[LCAyz])∗2−max(dep[LCAxy],dep[LCAxz],dep[LCAyz])
AC CODE
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+123;
int n,m,sum=0;
int f[N][24],dep[N],md=-N;
struct node{
vector<int> ve;
}a[N];
void dfs(int fa,int cnt,int dept){
if(dep[cnt]!=0)return;
dep[cnt]=dept;
f[cnt][0]=fa;
md=max(md,dept);
for(auto &x:a[cnt].ve){
if(dep[x]!=0)continue;
dfs(cnt,x,dept+1);
}
}
void LCA(){
f[1][0]=1;
for(int i=1;(1<<i)<=md;i++){
for(int j=1;j<=n;j++){
f[j][i]=f[f[j][i-1]][i-1];
}
}
}
int query(int x,int y){
sum=0;
if(dep[x]>dep[y])swap(x,y);
for(int i=23;i>=0;i--){
if(dep[f[y][i]]>=dep[x]){
y=f[y][i];
sum+=(1<<i);
}
}
if(x==y)return x;
for(int i=23;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
sum=sum+(1<<i)*2;
}
}
return f[x][0];
}
int main(){
cin>>n>>m;
for(int i=1,aa,bb;i<n;i++){
cin>>aa>>bb;
a[aa].ve.push_back(bb);
a[bb].ve.push_back(aa);
}
dfs(1,1,1);
LCA();
for(int i=1;i<=m;i++){
int x,y,z;
// cin>>x>>y>>z;
scanf("%d%d%d",&x,&y,&z);
int lxy=query(x,y);
int lyz=query(y,z);
int lxz=query(x,z);
int ans;
ans=dep[x]+dep[y]+dep[z]-
min(dep[lxy],min(dep[lxz],dep[lyz]))*2
-max(dep[lxy],max(dep[lxz],dep[lyz]));
if(lxy==lxz){//root==y
// cout<<lyz<<" "<<ans<<endl;
printf("%d %d\n",lyz,ans);
}
else if(lxy==lyz){
// cout<<lxz<<" "<<ans<<endl;
printf("%d %d\n",lxz,ans);
}
else if(lxz==lyz){
// cout<<lxy<<" "<<ans<<endl;
printf("%d %d\n",lxy,ans);
}
}
return 0;
}
/*6 4
1 2
2 3
2 4
4 5
5 6
6 3 1*/