P3398 仓鼠找sugar

题目
题意
判定树上的两条路径是否有公共点。
算法
lca

分析
假设两条路径有公共点,那么公共点中必定包括路径1的某一端点路径2的某一端点最近公共祖先
但至于是哪一对就不得而知了
那为什么是这样呢
反证法
若两条路径都不经过任意一对点的最近公共祖先
如下图
在最近公共祖先的两棵子树上,老死不相往来。
在这里插入图片描述
有了这个基础,
我们要如何判定两条路径都经过了lca呢
现在介绍一个船新的方法–判定一个点是否在一条路径上
设dis(i,j)为i,j两点的最短距离
设u,v为路径的两端点。
设k为判定点
则满足dis(k,u)+dis(k,v)==dis(u,v)
k点在以u,v为两端点的路径上。
证明可用反证法
接下来上代码

#include<bits/stdc++.h>
using namespace std;
const int ll=3e5;
int n,m,tot,t;
int head[ll],next[ll],ver[ll];
int d[ll],f[ll][20],fa[ll],dis[ll];
void add(int u,int v){
	tot++;
	ver[tot]=v;
	next[tot]=head[u];
	head[u]=tot;
}
int lca(int x,int y){
	if(d[x]>d[y]) swap(x,y);
	for(int i=t;i>=0;i--)
		if(d[f[y][i]]>=d[x])
			y=f[y][i];
	if(x==y) return x;
	for(int i=t;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
bool pd(int a,int b,int c,int len){
	return 2*dis[a]+dis[b]+dis[c]-2*(dis[lca(a,b)]+dis[lca(a,c)])==len;	
}
int main(){
	scanf("%d%d",&n,&m);
	t=log(n)/log(2)+1;
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v); add(v,u);
	}
	queue <int> q;
	q.push(1);
	d[1]=1;
	while(q.size()){
		int x=q.front(); q.pop();
		for(int i=head[x]; i ;i=next[i]){
			int v=ver[i];
			if(d[v]) continue;
			d[v]=d[x]+1;
			f[v][0]=x;
			dis[v]=dis[x]+1;
			fa[v]=x;
			for(int j=1;j<=t;j++)
				f[v][j]=f[f[v][j-1]][j-1];
			q.push(v);	
		}
	}
	for(int i=1;i<=m;i++){
		int a,b,c,end;		
		scanf("%d%d%d%d",&a,&b,&c,&end);
		if(d[a]<d[b]) swap(a,b);
		if(d[c]<d[end]) swap(c,end);
		int len1=dis[a]+dis[b]-2*dis[lca(a,b)];
		int len2=dis[c]+dis[end]-2*dis[lca(c,end)];
		int father=lca(a,c);
		if(pd(father,a,b,len1)&&pd(father,c,end,len2)){
			printf("Y\n");continue;}
		father=lca(a,end);	
		if(pd(father,a,b,len1)&&pd(father,c,end,len2)){
			printf("Y\n");continue;}
		father=lca(b,end);	
		if(pd(father,a,b,len1)&&pd(father,c,end,len2)){
			printf("Y\n");continue;}
		father=lca(b,c);	
		if(pd(father,a,b,len1)&&pd(father,c,end,len2)){
			printf("Y\n");continue;}	
		printf("N\n");
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值