[bzoj1787]紧急集合
对于三个点x、y和z,设a=lca(x,y),b=lca(x,z),不妨假设a的深度比b大,则b是a的祖先(因为a是x的祖先,b也是x的祖先)。
首先将集合点定在a,考虑a向每一个方向调整,a向父亲调整会让x和y答案加一,a向靠近x(y同理)的儿子调整,会让y和z答案加一,向其他儿子调整,会让三者答案都加一,由此发现a是最合适的点(这个过程似乎并不是很严谨,可以理解就行)。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 500005 4 struct ji{ 5 int nex,to; 6 }edge[N<<1]; 7 int E,n,m,x,y,z,head[N],in[N],out[N],s[N],f[N][21]; 8 bool pd(int x,int y){ 9 return (in[x]<=in[y])&&(out[y]<=out[x]); 10 } 11 int lca(int x,int y){ 12 if (pd(x,y))return x; 13 for(int i=20;i>=0;i--) 14 if (!pd(f[x][i],y))x=f[x][i]; 15 return f[x][0]; 16 } 17 int len(int x,int y){ 18 return s[x]+s[y]-2*s[lca(x,y)]; 19 } 20 void add(int x,int y){ 21 edge[E].nex=head[x]; 22 edge[E].to=y; 23 head[x]=E++; 24 } 25 void dfs(int k,int fa,int sh){ 26 in[k]=++x; 27 s[k]=sh; 28 f[k][0]=fa; 29 for(int i=1;i<=20;i++)f[k][i]=f[f[k][i-1]][i-1]; 30 for(int i=head[k];i!=-1;i=edge[i].nex) 31 if (edge[i].to!=fa)dfs(edge[i].to,k,sh+1); 32 out[k]=++x; 33 } 34 int main(){ 35 scanf("%d%d",&n,&m); 36 memset(head,-1,sizeof(head)); 37 for(int i=1;i<n;i++){ 38 scanf("%d%d",&x,&y); 39 add(x,y); 40 add(y,x); 41 } 42 x=0; 43 dfs(1,1,0); 44 for(int i=1;i<=m;i++){ 45 scanf("%d%d%d",&x,&y,&z); 46 int k=(lca(x,y)^lca(x,z)^lca(y,z)); 47 printf("%d %d\n",k,len(x,k)+len(y,k)+len(z,k)); 48 } 49 }