[bzoj1787]紧急集合

对于三个点xyz,设a=lca(x,y),b=lca(x,z),不妨假设a的深度比b大,则ba的祖先(因为ax的祖先,b也是x的祖先)。

首先将集合点定在a,考虑a向每一个方向调整,a向父亲调整会让xy答案加一,a向靠近x(y同理)的儿子调整,会让yz答案加一,向其他儿子调整,会让三者答案都加一,由此发现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 }
View Code

 

posted @ 2019-07-28 10:41  PYWBKTDA  阅读(175)  评论(0编辑  收藏  举报