BZOJ1787 [Ahoi2008]Meet 紧急集合 LCA
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1787
题意概括
有一棵节点为n个(n≤500000)的树。接下来m次询问(m≤500000),每次给出3个点 a,b,c ,现在让你求一个点 p ,使得 dis(p,a) + dis(p,b) + dis(p,c) 最小。
输出 p 和 dis(p,a) + dis(p,b) + dis(p,c)。
题解
分别求3个LCA。
学习LCA -> 传送门
有两个一样的,那么另外一个就是答案。
代码
#pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstring> #include <algorithm> #include <cstdio> #include <cmath> #include <cstdlib> using namespace std; const int N=500000+5,M=N*2; struct Gragh{ int cnt,y[M],nxt[M],fst[N]; void set(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int n,m,depth[N],anst[N][20]; void dfs(int prep,int rt){ depth[rt]=depth[anst[rt][0]=prep]+1; for (int i=1;i<20;i++) anst[rt][i]=anst[anst[rt][i-1]][i-1]; for (int i=g.fst[rt];i;i=g.nxt[i]) if (g.y[i]!=prep) dfs(rt,g.y[i]); } int LCA(int a,int b){ if (depth[a]>depth[b]) swap(a,b); for (int j=depth[b]-depth[a],i=0;j>0;j>>=1,i++) if (j&1) b=anst[b][i]; if (a==b) return a; for (int i=19;i>=0;i--) if (anst[a][i]!=anst[b][i]) a=anst[a][i],b=anst[b][i]; return anst[a][0]; } int main(){ scanf("%d%d",&n,&m); g.set(); for (int i=1,a,b;i<n;i++){ scanf("%d%d",&a,&b); g.add(a,b); g.add(b,a); } depth[0]=-1; memset(anst,0,sizeof anst); dfs(0,1); for (int i=1,a,b,c,ans,pos;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); int p1=LCA(a,b),p2=LCA(a,c),p3=LCA(b,c); if (p1==p2) pos=p3; else if (p1==p3) pos=p2; else pos=p1; int q1=LCA(pos,a),q2=LCA(pos,b),q3=LCA(pos,c); ans=depth[a]+depth[b]+depth[c]+depth[pos]*3-depth[q1]*2-depth[q2]*2-depth[q3]*2; printf("%d %d\n",pos,ans); } return 0; }