LCA应用_P3398 仓鼠找 sugar

2023/4/15
题目大意就是给定一棵树,给定4个点 \(abcd\),求判断 a到b,c到d的路径,是否有相交?
考虑 lca 的应用
本题的关键点在一个结论:如果两条路径相交,一定有x在c到d的路上,或y在a到b的路上(二者至少满足其一),反过来,如果该条件成立的话,一定有两条路径相交。
如何证明呢,上图

证明采用反证法,
假设在路径相交时,lca(a,b),即z,不在c到d的路线上;且 lca(c,d),即y,不在a到b的路线上。
就会形成如上图的结构。
显然我们观察到x点有两个父亲节点,而这不符合树的性质,矛盾,假设不成立。
因此我们可以得出来正确的树的结构,如下图所示。

我们可以发现,x 才是真正的lca(a,b),它在c到d的路径上,也符合我们要证的结论,证毕。
当x在c到d的路径上时有这样的表达式。
((Lca(x,c)==x)||(Lca(x,d)==x))&&Lca(x,y)==y
解释: x是c或d的父亲(满足一个),且y是x的父亲,那么也就是说 x在 c或d 到 y 的中间部分
(y在a b 上的存在性同上)
代码如下:

#include<iostream>
#define maxn 100001
using namespace std;
int f[maxn][22],dep[maxn],n,q,a,b,c,d,x,y,head[maxn],cnt;
struct Edge{ int v,nxt; }edge[maxn<<1];
void add(int u,int v){ edge[++cnt].v=v,edge[cnt].nxt=head[u],head[u]=cnt; }
void dfs(int u,int fa){
	dep[u]=dep[fa]+1,f[u][0]=fa;
	for (int i=1;(1<<i)<=dep[u];i++){
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for (int i=head[u];i;i=edge[i].nxt){
		if (edge[i].v==fa) continue;
		dfs(edge[i].v,u);
	}
}
int LCA(int u,int v){
	if (u==v) return v;
	if (dep[u]<dep[v]) swap(u,v);
	for (int i=21;i>=0;i--){
		if (dep[f[u][i]]>=dep[v]) u=f[u][i];
		if (dep[u]==dep[v]) break;
	}
	if (u==v) return v;
	for (int i=21;i>=0;i--){
		if (f[u][i]==f[v][i]) continue;
		u=f[u][i],v=f[v][i];
	}
	return f[u][0];
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>q;
	for (int i=1;i<n;i++){
		cin>>a>>b; add(a,b),add(b,a);
	}
	dfs(1,0);
	for (int i=1;i<=n;i++){
		cin>>a>>b>>c>>d;
		x=LCA(a,b),y=LCA(c,d);
		if ((((LCA(x,c)==x)||(LCA(x,d)==x))&&LCA(x,y)==y) || (((LCA(y,a)==y)||(LCA(y,b)==y))&&LCA(x,y)==x)){
			puts("Y");
		}
		else puts("N");
	}
	return 0;
}
posted @ 2023-04-15 13:03  l晨曦礼赞l  阅读(27)  评论(0)    收藏  举报