lca的应用 P4281 [AHOI2008] 紧急集合 / 聚会
传送门 : P4281 [AHOI2008] 紧急集合 / 聚会
题目大意
给出一棵树,以及给出m个询问:每个询问包括a,b,c三个数,在树上找一个点使得三点到该点的距离和最小。
我的做法是
由于在树上求两点之间的距离会用到lca,所以就往这个方面想了一下。
发现这个点要在a,b,c之间才行,更具体的这个点是它们两两之间的lca的深度最大的那一个。
所以就直接敲了个很朴素的计算,结果tle了,如下
int sum = 0;
sum += dep[a] + dep[l] - 2*dep[lca(a,l)];
sum += dep[b] + dep[l] - 2*dep[lca(b,l)];
sum += dep[c] + dep[l] - 2*dep[lca(c,l)];
后面想怎么样简化这个计算才慢慢发现了问题的本质:
如果这个点不在我要求的这个点上面的话,通过画一下路径发现它是会走多一段的,而在我要找到这个点上面的时候,刚好是等于他们两两之间的距离的和的一半。
也就是 sum = dep[a] + dep[b] + dep[c] - dep[l1] - dep[l2] - dep[l3];
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+100;
int n,m;
vector<int>e[N];
int f[N][20],dep[N];
void dfs(int u,int fa)
{
f[u][0] = fa;
dep[u] = dep[fa]+1;
for(int i = 1 ; i < 20 ; ++i)
f[u][i] = f[f[u][i-1]][i-1];
for(int v : e[u])
{
if(v == fa)continue;
dfs(v,u);
}
}
int lca(int u,int v)
{
if(dep[u] < dep[v])swap(u,v);
for(int i = 19 ; ~i ; --i)
if(dep[f[u][i]] >= dep[v])
u = f[u][i];
if(u == v)return v;
for(int i = 19 ; ~i ; --i)
if(f[u][i] != f[v][i])u = f[u][i],v = f[v][i];
return f[u][0];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i = 1 ; i < n ; ++i)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);e[v].push_back(u);
}
dfs(1,0);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
int l1 = lca(a,b),l2 = lca(b,c),l3 = lca(a,c);
int l = 0;
if(dep[l1] > dep[l])l = l1;
if(dep[l2] > dep[l])l = l2;
if(dep[l3] > dep[l])l = l3;
int sum = 0;
sum = dep[a] + dep[b] + dep[c] - dep[l1] - dep[l2] - dep[l3];
cout<<l<<" "<<sum<<endl;
}
return 0;
}
浙公网安备 33010602011771号