题解 CF1304E 【1-Trees and Queries】

前言

这场比赛,在最后 5 5 5 分钟,我想到了这道题的 I d e a Idea Idea,但是,没有打完,比赛就结束了。

正文

题目意思

这道题目的意思就是说,一棵树上每次给 x x x y y y 节点连 1 1 1 条边,问 a a a b b b 之间有没有长度为 k k k 的边。

分析

一开始,我看到这道题就往基环树这里去想,可实际上,这道题的方法却是和加工零件这道题是有异曲同工之处,作者那道题里面也写了篇题解,不会的同学可以去看一看。

这道题难处理的地方就是加 1 1 1 条边这个地方很难处理,但是我们可以想一想,实际上可能的路径一共就3条

  1. a    ⟹    b a \implies b ab 这是最原始的路径。

  2. a    ⟹    x    ⟹    y    ⟹    b a \implies x \implies y \implies b axyb 这是借助 x , y x,y x,y 的路径

  3. a    ⟹    y    ⟹    x    ⟹    b a \implies y \implies x \implies b ayxb 这是借助 y , x y,x y,x 的路径。

也就是

bool check(int x,int y){
    if(x<=y&&x%2==y%2)return true;
    return false;
}
while(T--){
    int x,y,a,b,k;
    read(x);read(y);read(a);read(b);read(k);
    int ab=dist(a,b),ax=dist(a,x),yb=dist(b,y),ay=dist(a,y),bx=dist(b,x);
    if(check(ab,k)||check(ax+yb+1,k)||check(ay+bx+1,k))puts("YES");
    else puts("NO");
}

处理往回走

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

  1. 路径长度 ≤ k \leq k k 这一个很显然。 长度 > k > k >k,显然就是不合法的。

  2. 路径长度和 k k k 奇偶性相同。 这就是基于往回走的做法,奇偶性相同,就代表两个数的差是偶数,所以就是可以组成长度为 k k k 路径。

预处理 2 2 2 点之间的距离

我们刚才说了,两个点之间的距离显然是要求出来的,我们需要预处理 L C A LCA LCA,不会的同学可以左转题解区,我用的是最朴素的倍增 L C A LCA LCA

void dfs(int x,int f){
    dep[x]=dep[f]+1;
    fa[x][0]=f;
    for(int i=1;(1<<i)<=dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=head[x];i;i=nxt[i])
		if(t[i]!=f)dfs(t[i],x);
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];
    if(x==y)return x;
    for(int k=lg[dep[x]]-1;k>=0;k--)
		if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];
    return fa[x][0];
}
int dist(int x,int y){//x号节点和y号节点的距离
    int z=lca(x,y);
    return dep[x]+dep[y]-dep[z]*2;
}

代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}//快读
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}//快写
int dep[500010],fa[500010][22],lg[500010],head[500010],nxt[500010],t[500010],tot;
void add(int x,int y){
    t[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}//连边
void dfs(int x,int f){
    dep[x]=dep[f]+1;
    fa[x][0]=f;
    for(int i=1;(1<<i)<=dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=head[x];i;i=nxt[i])
		if(t[i]!=f)dfs(t[i],x);
}//预处理father
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];
    if(x==y)return x;
    for(int k=lg[dep[x]]-1;k>=0;k--)
		if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];
    return fa[x][0];
}//LCA
int dist(int x,int y){
    int z=lca(x,y);
    return dep[x]+dep[y]-dep[z]*2;
}//x、y两点之间的距离
bool check(int x,int k){
    if(x<=k&&x%2==k%2)return true;
    return false;
}//检查长度为x的边是否满足前文讲得2个条件
int main(){
	int n;
    read(n);
    for(int i=1;i<n;i++){
        int x,y;
        read(x);read(y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);//预处理除log
    int T;
    read(T);
    while(T--){
        int x,y,a,b,k;
        read(x);read(y);read(a);read(b);read(k);
        int ab=dist(a,b),ax=dist(a,x),yb=dist(b,y),ay=dist(a,y),bx=dist(b,x);//3条边
        if(check(ab,k)||check(ax+yb+1,k)||check(ay+bx+1,k))puts("YES");//有1条符合条件,就代表有
        else puts("NO");//3条都不符合就代表没有
    }
    return 0;
}

后记

这道题还是很有思考的价值,也算是积累了经验看到一棵树加 1 1 1 条边,未必一定要往基环树想。希望觉得好的同学可以点赞,有问题请在评论区表述一下,是我的题解都够再完善一下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值