题意:传送门
题解:分析从小到大分析,从简单到复杂分析,或许就能启发,如果考虑两个点的话那么这两个点的lca绝对是最优解,其实只要是这两个点路径中的任意一点都可以,到了三个点,那么怎么能使得路径最短,如果跑的路中有多余重复的路(就是比如说a跑过这一段,b还会跑这一段),那么肯定这个就不是最优解,同时可以知道的是这样两两会求出一个lca,也就是下来总共三个lca,可以想到的是一定有两个lca是一样的,并且这个lca是深度最低的,与此同时另外一个lca一定就是最优解了,可以画图验证,也可以用反证法验证,最后要输出总路径,也就是利用差分的思想,假设我们这三个点是a,b,c,求出的三个lca是a1,b1,c1,假设a1为答案,那么:
也就是:这是一个轮换式。
这个题就是直接查询,用树链剖分可以,用倍增也可以。
附上代码(树链剖分):
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=5e5+5;
const int maxm=1e6+5;
const int inf=0x7fffffff;
struct edge{int v,next;}e[maxm];
int head[maxn],tot;
void add_edges(int u,int v)
{
e[++tot].v=v;e[tot].next=head[u];head[u]=tot;
e[++tot].v=u;e[tot].next=head[v];head[v]=tot;
}
int n,m,a,b,c,ans;
int size[maxn],dep[maxn],fa[maxn];
int sz,pos[maxn],bl[maxn];
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add_edges(x,y);
}
}
void dfs1(int u)
{
size[u]=1;
for(int i=head[u];i;i=e[i].next)
{
if(e[i].v==fa[u])continue;
dep[e[i].v]=dep[u]+1;
fa[e[i].v]=u;
dfs1(e[i].v);
size[u]+=size[e[i].v];
}
}
void dfs2(int u,int chain)
{
int k=0;sz++;
pos[u]=sz;
bl[u]=chain;
for(int i=head[u];i;i=e[i].next)
if(dep[e[i].v]>dep[u]&&size[e[i].v]>size[k])
k=e[i].v;
if(k==0)return;
dfs2(k,chain);
for(int i=head[u];i;i=e[i].next)
if(dep[e[i].v]>dep[u]&&k!=e[i].v)
dfs2(e[i].v,e[i].v);
}
int lca(int x,int y)
{
while(bl[x]!=bl[y])
{
if(dep[bl[x]]<dep[bl[y]])swap(x,y);
x=fa[bl[x]];
}
if(pos[x]>pos[y])swap(x,y);
return x;
}
void solve()
{
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
int a1=lca(a,b),b1=lca(a,c),c1=lca(b,c);
if(a1==b1)ans=c1;
else if(a1==c1)ans=b1;
else ans=a1;
printf("%d %d\n",ans,dep[a]+dep[b]+dep[c]-dep[a1]-dep[b1]-dep[c1]);
}
}
int main()
{
init();
dfs1(1);
dfs2(1,1);
solve();
return 0;
}
附上代码(倍增)(加了读入优化):
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
const int maxn=5e5+5;
const int maxm=1e6+5;
struct edge{int v,next;}e[maxm];
int head[maxn],tot;
void add_edges(int u,int v)
{
e[++tot].v=v;e[tot].next=head[u];head[u]=tot;
e[++tot].v=u;e[tot].next=head[v];head[v]=tot;
}
int n,m;
int fa[maxn][20],deep[maxn];
bool vis[maxn];
void init()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int x,y;
x=read();y=read();
add_edges(x,y);
}
}
void dfs(int u)
{
vis[u]=true;
for(int i=1;i<=18;i++)
{
if(deep[u]<(1<<i))break;
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=head[u];i;i=e[i].next)
{
if(vis[e[i].v])continue;
deep[e[i].v]=deep[u]+1;
fa[e[i].v][0]=u;
dfs(e[i].v);
}
}
int lca(int x,int y)
{
if(deep[x]<deep[y])swap(x,y);
int d=deep[x]-deep[y];
for(int i=0;i<=18;i++)
if((d>>i)&1)x=fa[x][i];
for(int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{x=fa[x][i];y=fa[y][i];}
if(x==y)return x;
else return fa[x][0];
}
void solve()
{
int a,b,c,ans;
for(int i=1;i<=m;i++)
{
a=read();b=read();c=read();
int a1=lca(a,b),b1=lca(a,c),c1=lca(b,c);
if(a1==b1)ans=c1;
else if(a1==c1)ans=b1;
else ans=a1;
printf("%d %d\n",ans,deep[a]+deep[b]+deep[c]-deep[a1]-deep[b1]-deep[c1]);
}
}
int main()
{
init();
dfs(1);
solve();
return 0;
}