题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1787
水题,但是结论很有趣。
题目求的是距离三个点之和最小的点。
这个很显然是在三个LCA上,因为可以证明除LCA的点是由LCA移动来的,这个过程中
如果在 (x,y) 移动,答案不变没有意义。
如果偏离 (x,y) 移动,答案不优。
然而有一个好玩的结论:若有两个点对 LCA 相同那么答案为另一个 LCA
画画图就可以发现,x 与 y 到另一个 LCA 的路径有重合,很显然的吧。
#include <cstdio> #include <cstring> #include <algorithm> #define N 500010 #define p E[i].x using namespace std; struct edge{ int x,to; }E[N<<1]; int n,m,totE,g[N],fa[N][21],d[N]; inline void addedge(int x,int y){ E[++totE]=(edge){y,g[x]}; g[x]=totE; } void dfs(int x){ for(int i=1;fa[x][i-1]&&i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=g[x];i;i=E[i].to) if(p!=fa[x][0]){ fa[p][0]=x; d[p]=d[x]+1; dfs(p); } } int lca(int x,int y){ if(d[x]<d[y]) swap(x,y); for(int i=20;~i;i--) if(d[fa[x][i]]>=d[y]) x=fa[x][i]; if(x==y) return x; for(int i=20;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int dist(int a,int b){ return d[a]+d[b]-2*d[lca(a,b)]; } void ask(int a,int b,int c){ int f1=lca(a,b),f2=lca(a,c),f3=lca(b,c),ans,ansv; if(f1==f2) ans=f3; else if(f2==f3) ans=f1; else ans=f2; ansv=dist(a,ans)+dist(b,ans)+dist(c,ans); printf("%d %d ",ans,ansv); } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } d[1]=1; dfs(1); for(int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); ask(x,y,z); } return 0; }