【LCA变式例题】—— Trees and Queries


题目

Description

给定一个 n 个点的树,相邻点的距离为 1。
q 个询问,每个询问包含五个整数: x,y,a,b,k。
含义是:在原树上新连上一条边 (x,y) ,要求判断一下从 a 点是否有距离为 k 的到 b 的路径。
注意:
每个询问是独立的,即上次询问加上的边,不能为这一次的询问所用。
这一条路径也许会重复经过某一条边或某一点。

Input

  • 第 1 行: n ;
  • 第 2 ⋯n 行: 每行 2个整数 u,v表示原树上有一条连接结点 u,v 的边。
  • 第 n+1行:q;
  • 接下来 q 行:每行五个整数: x,y,a,b,k意义见上。

Output

  • 针对每个询问,如果可以输出“YES”,否则输出“NO”

输入/输出例子

输入

5
1 2
2 3
3 4
4 5
5
1 3 1 2 2
1 4 1 3 2
1 4 1 3 3
4 2 3 3 9
5 2 3 3 9

输出

YES
YES
NO
YES
NO

提示


解题思路

分析

这道题难处理的地方就是加 1条边这个地方很难处理,但是我们可以想一想,实际上可能的路径一共就3条。
1. a⟹b 这是最原始的路径。
2. a⟹x⟹y⟹b 这是借助 x,y 的路径
3. a⟹y⟹x⟹b 这是借助 y,x 的路径。

初始化

和普通的lca一样:

struct fy
{
	int t,next;
}edge[N];
void add(int x,int y)
{
    edge[++cnt].t=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
void dfs(int u,int fa)
{
    d[u]=d[fa]+1,f[u][0]=fa;
    for(int i=1;i<=lg[d[u]];i++)
		f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=edge[i].next)
		if(edge[i].t!=fa)
			dfs(edge[i].t,u);
}
int lca(int x,int y)
{
    if(d[x]<d[y])
		swap(x,y);
    while(d[x]>d[y])
		x=f[x][lg[d[x]-d[y]]-1];
    if(x==y)
		return x;
    for(int i=lg[d[x]]-1;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
    return f[x][0];
}
scanf("%d",&n);
   for(int i=1;i<n;i++)
       scanf("%d%d",&x,&y),add(x,y),add(y,x);
   for(int i=1;i<=n;i++)
	lg[i]=lg[i-1]+(1<<lg[i-1]==i);
dfs(1,0);

处理回路

可能有有人会问,走到 1 个点,再走回来,这个怎么办呢?我们发现走到 1 个点再回来,这样 1 次路径长度是 2。所以我们这 3 条路径当中,只要有 1 条路径满足以下两个个条件,就说明存在这样一条长度为 k 的路径。

1.路径长度≤k 这一个很显然。当长度 > k时 ,显然就是不可能的。

2.(k-路径长度)%2==0。 这就是基于往回走的做法,差值为偶,就是可以组成长度为 k 路径,如图:在这里插入图片描述
它们之间的最短路径为3,差值为2,为偶数
所以可以达成:g->e->b->d ->b->d ->BOON!

代码语言就是:

int ga(int x,int y)
{
    return d[x]+d[y]-d[lca(x,y)]*2;
}
bool check(int x,int k)
{
	return x<=k&&(k-x)%2==0?true:false;
}
	
	
	for(int i=1;i<=m;i++)
	{
        scanf("%d%d%d%d%d",&x,&y,&a,&b,&k);
        int ab=ga(a,b),axyb=ga(a,x)+ga(b,y)+1,ayxb=ga(a,y)+ga(b,x)+1;
        if(check(ab,k)||check(axyb,k)||check(ayxb,k))
			printf("YES\n");
        else 
			printf("NO\n");
    }

及:考虑三种情况,分别check

code

#include <bits/stdc++.h>
using namespace std;
const int N=1e6;
int d[N],f[N][22],lg[N],head[N],cnt,n,m,x,y,a,b,k;
struct fy
{
	int t,next;
}edge[N];
void add(int x,int y)
{
    edge[++cnt].t=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
void dfs(int u,int fa)
{
    d[u]=d[fa]+1,f[u][0]=fa;
    for(int i=1;i<=lg[d[u]];i++)
		f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=edge[i].next)
		if(edge[i].t!=fa)
			dfs(edge[i].t,u);
}
int lca(int x,int y)
{
    if(d[x]<d[y])
		swap(x,y);
    while(d[x]>d[y])
		x=f[x][lg[d[x]-d[y]]-1];
    if(x==y)
		return x;
    for(int i=lg[d[x]]-1;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
    return f[x][0];
}
int ga(int x,int y)
{
    return d[x]+d[y]-d[lca(x,y)]*2;
}
bool check(int x,int k)
{
	return x<=k&&(k-x)%2==0?true:false;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    for(int i=1;i<=n;i++)
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	dfs(1,0);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
	{
        scanf("%d%d%d%d%d",&x,&y,&a,&b,&k);
        int ab=ga(a,b),axyb=ga(a,x)+ga(b,y)+1,ayxb=ga(a,y)+ga(b,x)+1;
        if(check(ab,k)||check(axyb,k)||check(ayxb,k))
			printf("YES\n");
        else 
			printf("NO\n");
    }
}

更多方法

更多方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值