[bzoj1787] [Ahoi2008]Meet 紧急集合

来自FallDream的博客,未经允许,请勿转载,谢谢qaq


给定一棵n个点的树,m个询问,每次给出3个点,求一个点使得这个点到三个点的距离和最小。n,m<=500000

题解:很容易想到求lca。稳健一点,就把三个点两两的lca求一下,计算答案;实际上我们发现一定有两个lca相同,我们就选另一个作为答案就行啦。

#include<iostream>
#include<cstdio>
#define MN 500000
#define MK 20
#define INF 2000000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,m,head[MN+5],dep[MN+5],cnt=0,from;
int fa[MN+5][MK+2];
struct edge{int to,next;}e[MN*2+5];

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

void dfs(int x,int f)
{
    fa[x][0]=f;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
            dep[e[i].to]=dep[x]+1,dfs(e[i].to,x);
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int k=dep[x]-dep[y],j=0;k;j++,k>>=1)
        if(k&1)x=fa[x][j];
    if(x==y)return x;
    for(int i=MK;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

inline int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}

int main()
{
    n=read();m=read();
    for(int i=1;i<n;i++) ins(read(),read());
    dfs(1,0);
    for(int j=1;j<=MK;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read(),c=read();from=0;
        int l1=lca(a,b),l2=lca(a,c),l3=lca(b,c);
        if(l1==l2)from=l3;
        else if(l1==l3)from=l2;
        else from=l1;
        printf("%d %d\n",from,dis(from,a)+dis(from,b)+dis(from,c));
    }
    return 0;
}

 

posted @ 2017-04-05 23:48  FallDream  阅读(271)  评论(0编辑  收藏  举报