P3398 仓鼠找sugar (LCA 性质)
完成本题要找到一个性质,要发现如何判断两条路径相交的条件。
找条件的最好方式莫过于研究实例了。接下来给出几个实例:
不难发现,红色路径总与蓝色路径相交,而且红色路径总是穿过蓝色路径的两端的 LCA.
于是我们可以猜测,当某一条路径经过另一条路径两端的 LCA 时,两条路径相交。
可以反证法证明:
假设在一棵树中,两条路径相交,相交的深度最浅结点不是其中一条路径的两端的 LCA .
那么意味着,相交最浅的结点都不是两条路径深度最浅的结点,于是这个结点将有两个父亲,与在一棵树中的条件矛盾,假设不成立,所以在一棵树中,两条路径相交的条件时某一条路径经过另一条路径深度最浅的结点,也就是路径两端的 LCA .
于是我们只要判断是否存在一条路径的最浅结点在另一条路径中就能得知二者是否相交。
判断方法也显然:
- 求出一条路径两端的 LCA ,令其为 P P P .
- 在另一条路径中,令其两端的结点为 u , v u,v u,v ,求 l c a ( u , P ) , l c a ( v , P ) {\rm lca}(u,P),{\rm lca}(v,P) lca(u,P),lca(v,P) ,求出的 LCA 中有一个是这条路径深度最浅的结点,有一个是 P P P 则证明 P P P 在这一条路径中。
bool Check(int x,int y,int lca_xy,int z)//倍增求 LCA
{
if(dep[z] < dep[lca_xy]) return 0;
if(dep[z] <= dep[x])
{
// if(x == z) return 1;
for(int i = 22;i >= 0;i --)
{
if(x == z) return 1;
if(dep[f[x][i]] >= dep[z]) x = f[x][i];
if(x == z) return 1;
}
}
if(dep[z] <= dep[y])
{
// if(y == z) return 1;
for(int i = 22;i >= 0;i --)
{
if(y == z) return 1;
if(dep[f[y][i]] >= dep[z]) y = f[y][i];
if(y == z) return 1;
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i = 1;i < n;i ++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
}
prework(1,0);
for(int i = 1;i <= q;i ++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
lca1 = GetLCA(a,b);//倍增求 LCA
lca2 = GetLCA(c,d);
flag = (Check(a,b,lca1,lca2) | Check(c,d,lca2,lca1));
if(flag) printf("Y\n");
else printf("N\n");
}
return 0;
}