zoukankan      html  css  js  c++  java
  • Borrow Classroom

    链接

    来源:牛客网

    https://ac.nowcoder.com/acm/contest/5086/C

    题目描述

    每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。

    现在把校园抽象成一棵n个节点的树,每条边的长度都是一个单位长度,从1到n编号,其中教务处位于1号节点,接下来有q个询问,每次询问中SK同学会从B号节点出发,到C号节点找到小Q同学并将借教室的单子交给他,然后小Q同学再从C号节点出发前往教务处,何老师会从A号节点出发开始拦截。

    所有人在一个单位时间内最多走一个单位距离,只要何老师在单子还没被送到教务处之前遇到拿着单子的同学都算拦截成功,如果小Q同学已经到了教务处,那么由于小Q同学手速极快,单子会被立即交上去,即使何老师到了教务处也无济于事,你需要判断何老师是否能够拦截成功。

    输入描述:

    第一行是一个正整数T(≤ 5),表示测试数据的组数, 对于每组测试数据, 第一行是两个整数n,q(1≤ n,q ≤ 100000),分别表示节点数和询问数, 接下来n-1行,每行包含两个整数x,y(1≤ x,y ≤ n),表示x和y之间有一条边相连,保证这些边能构成一棵树, 接下来q行,每行包含三个整数A,B,C(1 ≤ A,B,C ≤ n),表示一个询问,其中A是何老师所在位置,B是SK同学所在位置,C是小Q同学所在位置,保证小Q同学初始不在教务处。
    

    输出描述:

    对于每个询问,输出一行,如果何老师能成功拦截则输出"YES"(不含引号),否则输出"NO"(不含引号)。
    

    示例1

    输入

    1
    7 2
    1 2
    2 3
    3 4
    4 7
    1 5
    1 6
    3 5 6
    7 5 6
    

    输出

    YES
    NO
    

    思路

    如果直接去想在哪个点拦住不容易。这样想:如果能够在Sk,Q的路线上的某个点(除1点)拦住,那么一定也能够不晚于Q先到达教务处。所以直接算老师到达教务处的距离s1和SK到Q,Q再到教务处的距离s2。

    如果s1<s2那么一定能拦住。

    如果s1=s2,那么需要判断当sk和老师的最近公共祖先不是1,那么虽然他们到1的距离相等,如下图(红色老师,蓝色SK,绿色Q)也能够再某个点上汇合,从而在学生到1之前拦住,可以发现距离相等时,由于Q和老师都向上走,只要老师和Q的最近公共祖先要不是1,就能在1之前汇合,也能拦住。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N=100010;
    int anc[N][20],dep[N];
    int h[N],e[N*2],nex[N*2],idx,n;
    void add(int u,int v){
        e[idx]=v;
        nex[idx]=h[u];
        h[u]=idx++;
    }
    int q[N];
    void bfs(){
        memset(dep,0x3f,sizeof dep);
        dep[0]=0,dep[1]=1;
        int hh=0,tt=0;
        q[tt++]=1;
        while(hh!=tt){
            int u=q[hh++];
            if(hh==N) hh=0;
            for(int i=h[u];~i;i=nex[i]){
                int v=e[i];
                if(dep[v]>dep[u]+1){
                    dep[v]=dep[u]+1;
                    q[tt++]=v;
                    if(tt==N) tt=0;
                    anc[v][0]=u;
                    for(int j=1;j<=18;++j){
                        anc[v][j]=anc[anc[v][j-1]][j-1];
                    }
                }
            }
        }
    }
    int lca(int x,int y){
        if(x==y) return x;
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=18;i>=0;--i){
            if(dep[anc[x][i]]>=dep[y]){
                x=anc[x][i];
            }
        }
        if(x==y) return x;
        for(int i=18;i>=0;--i){
            if(anc[x][i]!=anc[y][i]){
                x=anc[x][i],y=anc[y][i];
            }
        }
        return anc[x][0];
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            memset(h,-1,sizeof h);idx=0;
            int q;
            scanf("%d%d",&n,&q);
            for(int i=1,x,y;i<n;++i){
                scanf("%d%d",&x,&y);
                add(x,y);
                add(y,x);
            }
            bfs();
            while(q--){
                int x,y,z;
                scanf("%d%d%d",&z,&x,&y);
                int lxy=dep[x]+dep[y]-dep[lca(x,y)]*2;
                int ly=dep[1]+dep[y]-dep[lca(1,y)]*2;
                int lz=dep[1]+dep[z]-dep[lca(1,z)]*2;
                if(lxy+ly>lz){
                    puts("YES");
                }
                else if(lxy+ly==lz){
                    if(lca(y,z)==1) {
                        puts("NO");
                    }
                    else puts("YES");
                }
                else  puts("NO");
            }
        }  
        return 0;
    }
    
  • 相关阅读:
    关于内存对齐的一点理解
    绿萝记
    《程序是怎样跑起来的》读书笔记——第五章 内存和磁盘的亲密关系
    《程序是怎样跑起来的》读书笔记——第四章 熟练使用有棱有角的内存
    《程序是怎样跑起来的》读书笔记——第三章 计算机进行小数运算时出错的原因
    《程序是怎样跑起来的》读书笔记——第二章 数据是用二进制数表示的
    《程序是怎样跑起来的》读书笔记——第一章 对程序员来说CPU是什么
    代理模式--学习记录
    latex学习--基础知识
    JavaIO之File类
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12843530.html
Copyright © 2011-2022 走看看