三点两两LCA性质定理的证明

严谨证明一个常识性的东西

主要是看大家做P4281时貌似并没有想到怎这么做,或是直接看了题解然后“感性理解”了

定理本身:

1.树上三个点的LCA不可能两两互异

即$\begin{cases}lca(a,b)≠lca(a,c)\\lca(b,c)≠lca(a,c)\\lca(a,b)≠lca(b,c)\end{cases}$ 不可能成立

2.这两个相同的的点以外的那点到三个点距离和最小 (其中三个点lca相同时另作讨论)

证:

引理1:如果a,b两个点在同一条末端在叶子节点上的链$l$上,那么对于 $ \forall c \notin l $ $ 上端所在的子树 $ ,必有

$ lca(a,c)=lca(b,c)=lca(a,b,c) $

引理2: $ lca(a,b,c)\equiv lca(a,lca(b,c)) $

令三个不全重合的点为a,b,c
不妨先对a,b,进行讨论

不妨设deep[a]>=deep[b]

1 a和b重合 那么$ lca(a,b)=a=b ,c≠a,b$ 所以$\begin{cases}lca(a,b)=a\\lca(b,c)=lca(a,c)\end{cases}$

2° a是b的后代 那么$lca(a,b)=b$

C的存在有4种情况,如图

① C在s1 (a的下方) 此时 $lca(a,b)=lca(b,c)=b$

② C在s2 (ab之间) 同理 此时$lca(a,b)=lca(b,c)=b$

③ C在s3(ab上方)此时$lca(a,c)=lca(b,c)=c$

④C在s4(另一棵子树)

因为a和b在同条链上 $lca(a,b)=b$ 那么$lca(a,c)$和$lca(b,c)$必相同(引理),即上图根结点.

3°a和b在两棵不同子树上 那么C的存在有5种情况(除去一种完全等价)

①C在s1 这种情况本质就是2°④
$lca(a,b)$和$lca(c,b)$相同(引理),为图中的m

②这个真的是同理 $lca(a,c)=lca(b,c)$(引理)

③在s3区域内 那么a,b中必有一个点不与c在一条链上那么这个点和c,另一点的lca一定相同(引理)

④在s4区域内那么c一定是a,b的公共祖先$lca(a,c)=lca(b,c)=c$

⑤在s5区域内 那么$lca(a,c)$一定为n即a所在子树与子树s5的共同父亲

$lca(b,c)$同理

$lca(a,c)=lca(b,c)=lca(a,b,c)=lca(lca(a,b),c)=lca(m,c)$

证毕

性质2

“翻译一下就是$lca(a,b)=lca(b,c)$那么$lca(a,c)$就是离a,b,c距离之和最小点。

$(1)$中得到了$lca(a,c)$是a,b,c三条路径的交汇点,(想象一下把这个点"提住"作为根)

那么假设这个点向朝a/b/c的简单路径移动w 那么$dis=(da-w)+(db+w)+(dc+w)$

$=dis+w >dis$

那这个点朝着其他方向移动w那么 dis=(da+w)+(db+w)+(dc+w)$ $=dis+3w >dis$

#include<bits/stdc++.h>
#define int long long
#define rg register
#define __max(aaa,bbb,ccc) max(max(aaa,bbb),ccc)
#define __min(aaa,bbb,ccc) min(min(aaa,bbb),ccc)

using namespace std;

const int N=5e5+5;

template <typename T> inline void read(T &x)
{
    x=0;int f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x*=f;
}

template <typename T> inline void print(T x)
{
    if(x>9) print(x/10);
    putchar(x%10+48);
}

int n,jump[N][22],deep[N],m,s;

vector<int> vct[N]; 

void add(int x,int y)
{
    vct[x].push_back(y);
    vct[y].push_back(x);
}

void dfa(int now ,int from)
{ 
    deep[now]=deep[from]+1;
    jump[now][0]=from;
    for(int j=1;j<=19;++j)
      jump[now][j]=jump[jump[now][j-1]][j-1];//往上跳2^j==往上跳2^(j-1)  从这个点再往上2^(j-1)
    for(int i=0;i<vct[now].size();++i)
    {
        int to=vct[now][i];
        if(to==from)continue;
        dfa(to,now);
    }

    return ;
}

int LCA(int x,int y)
{
    if(deep[x]<deep[y])swap(x,y);//x is deeper than y
//  for(;deep[x]>=deep[y];x=jump[x][(int)log2(deep[y]-deep[x]+1)]); 
    for(int i=19;i>=0;--i)
        if(deep[jump[x][i]]>=deep[y])
        x=jump[x][i];
//  cout<<"x:" <<x<<"y:"<<y<<endl;
    //统一深度
    if(x==y)return x;
    for(int i=19;i>=0;--i)//从后往前“二分”
    {
        if(jump[x][i]!=jump[y][i])
            x=jump[x][i],
            y=jump[y][i];
    }
    return jump[x][0];    
//return x;
}
signed  main()
{
      cin>>n>>m;
      s=1;deep[s]=1;
      int x,y,z;
      for(int i=1;i<n;++i)
        {

            read(x);
            read(y);
            add(x,y);
        }
    dfa(s,0);
      for(int i=1;i<=m;++i)
        {
            read(x);
            read(y);
            read(z);
            int l1=LCA(x,y);
            int l2=LCA(x,z);
            int l3=LCA(y,z);
            int ans;

        //  printf("%lld %lld %lld\n",l1,l2,l3);
            if(l1==l2) ans=l3;else
            if(l2==l3) ans=l1;else
            if(l3==l1) ans=l2;    
            cout<<ans<<' '<<deep[x]+deep[y]+deep[z]-deep[l1]-deep[l2]-deep[l3]<<endl;  
        }   
}
posted @ 2021-07-23 19:14  寂静的海底  阅读(17)  评论(0编辑  收藏  举报  来源