【BZOJ1832】【AHOI2008】聚会 &【BZOJ1787】Meet 紧急集合

Description

给出一颗树,求点X使点x,y,z到它的距离最短,并求出来。

Solution

先把x,y,z的两两LCA求出来,
可以证明一定至少会有两个LCA使一样的,而X点就是与其他两个不同的那个,
于是就可以乱搞一波,
(本来想偷懒,结果WA了…QAQ)
复杂度: O(mlog2(n))

Code

#include<cstdio>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0]) 
#define iffa if(B[i][1]!=fa)
using namespace std;
const int N=500500,M=20;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m,ans,ans1;
int ac[N];
int g[N][M+1];
int A[N],B[2*N][2],B0;
void link(int q,int w)
{
    B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w;
    B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q;
}
void dfs(int q,int fa,int e)
{
    g[q][0]=fa;ac[q]=e;
    efo(i,q)iffa dfs(B[i][1],q,e+1);
}
int LCA1(int q,int w)
{
    while(ac[q]>w)
    {
        int i=0;
        while(ac[g[q][i+1]]>=w)i++;
        q=g[q][i];
    }
    return q;
}
int LCA(int q,int w)
{
    while(q!=w)
    {
        int i=0;
        while(g[q][i+1]!=g[w][i+1])i++;
        q=g[q][i];w=g[w][i];
    }
    return q;
}
int lca(int q,int w)
{
    q=LCA1(q,ac[w]);w=LCA1(w,ac[q]);
    return LCA(q,w);
}
int main()
{
    int q,w,e,m_;
    read(n),read(m_);
    fo(i,1,n-1)read(q),read(w),link(q,w);
    dfs(1,0,1);
    fo(j,1,M)fo(i,1,n)g[i][j]=g[g[i][j-1]][j-1];
    while(m_--)
    {
        read(q),read(w),read(e);
        int t=lca(q,w),t1=lca(w,e),t2=lca(q,e);
        if(t==t1)ans=t2,ans1=ac[q]+ac[e]-ac[t2]+ac[w]-2*ac[t];
            else if(t==t2)ans=t1,ans1=ac[w]+ac[e]-ac[t1]+ac[q]-2*ac[t];
                else ans=t,ans1=ac[w]+ac[q]-ac[t]+ac[e]-2*ac[t1];
        printf("%d %d\n",ans,ans1);
    }
    return 0;
}

你可能感兴趣的:(图论,LCA)