codeforces 620(Div.2)E.1-Trees and Queries

题目链接:620E
题意:给定一颗1e5规模的边权都为1的树,接下来1e5次询问,询问间独立,每次添加一条树中不存在的边,再给定两点询问两点间的路径长度是否可以达到k,两点间的路径可以多次包含相同点,相同边,可以来回走。

这个题和我以前做过的一个题很像,忘记题号了,大概是给一个n点n边的图,询问两点最短路,只需要删掉一条边形成树,按下面那三种情况即可。
一颗树的话两点间的最短路径是唯一的,所以如果没有新的边的话,只要路径长度是小于等于询问值,且两者的奇偶性相同则说明可以达到该询问值。
如果加入一条新边,那么必然会形成一个环,删掉该边还是原来的树,于是只要考虑路径是否经过该边即可。
于是预处理lca,节点深度,就可以知道两点最短路径长度。
比如在x,y间添加一条边,询问a,b之间。
就可以分三种情况:

  1. a->b
  2. a->x,x->y,y->b
  3. a->y,y->x,x->b

只需要检查这三种路径长度是否有满足情况即可。

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const int maxn=1e5+7;
int fa[maxn][19];
struct Edge{
    int v,next;
}edge[maxn<<1];
int head[maxn],top;
void add(int u,int v){
    edge[top].v=v;
    edge[top].next=head[u];
    head[u]=top++;
}
int dep[maxn];
void dfs(int u,int last){
    fa[u][0]=last;
    dep[u]=dep[last]+1;
    for(int i=1;i<18;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==last) continue;
        dfs(v,u);
    }
}
 
int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=18;i>=0;--i)
        if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=18;i>=0;--i)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
 
int getlen(int x,int y){
    int lca=LCA(x,y);
    return dep[x]+dep[y]-2*dep[lca];
}
bool check(int len,int k){
    return len<=k&&(((len%2)^(k%2))==0);
}
int main(){
    int n,u,v,x,y,a,b,k;
    top=0;memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs(1,0);
    int q;
    cin>>q;
    while(q--){
        scanf("%d%d%d%d%d",&x,&y,&a,&b,&k);
        int len=getlen(a,b);
        bool f=0;
        if(check(len,k)) f=1;
        else{
            len=getlen(x,a)+getlen(y,b)+1;
            if(check(len,k)) f=1;
            len=getlen(x,b)+getlen(y,a)+1;
            if(check(len,k)) f=1;
        }
        printf("%s\n",(f?"YES":"NO"));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值