天天看點

bzoj 1787: [Ahoi2008]Meet 緊急集合 lca題意分析代碼

題意

給定一棵樹,每次給出三個點,求一個點使得這三個點到該點的距離和最小。

n,q<=500000

分析

答案就是兩兩lca中深度最大的那個。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=;

int n,m,size[N],last[N],cnt,dep[N],fa[N],top[N];
struct edge{int to,next;}e[N*2];

int read()
{
    int x=,f=;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void addedge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

void dfs1(int x)
{
    size[x]=;dep[x]=dep[fa[x]]+;
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x]) continue;
        fa[e[i].to]=x;
        dfs1(e[i].to);
        size[x]+=size[e[i].to];
    }
}

void dfs2(int x,int chain)
{
    top[x]=chain;int k=;
    for (int i=last[x];i;i=e[i].next)
        if (e[i].to!=fa[x]&&size[e[i].to]>size[k]) k=e[i].to;
    if (!k) return;
    dfs2(k,chain);
    for (int i=last[x];i;i=e[i].next)
        if (e[i].to!=fa[x]&&e[i].to!=k) dfs2(e[i].to,e[i].to);
}

int get_lca(int x,int y)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}

int main()
{
    n=read();m=read();
    for (int i=;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y);
    }
    dfs1();
    dfs2(,);
    while (m--)
    {
        int x=read(),y=read(),z=read();
        int l1=get_lca(x,y),l2=get_lca(x,z),l3=get_lca(y,z);
        if (dep[l2]>dep[l1]) swap(l1,l2);
        if (dep[l3]>dep[l1]) swap(l1,l3);
        printf("%d %d\n",l1,dep[x]+dep[y]+dep[z]-dep[l1]-dep[l2]*2);
    }
    return ;
}
           

繼續閱讀