BZOJ1787 meet

题目: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\n",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;
}
View Code

 

posted @ 2015-06-03 21:41  lawyer'  阅读(160)  评论(0编辑  收藏  举报