三点两两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;
}
}
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17971019,谢谢你的阅读或转载!