bnu52509-LCA

题目链接:bnu52509

Borrow Classroom

5000ms
262144KB
64-bit integer IO format:  %lld      Java class name:  Main
Font Size:   
Type: 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •                    
  • 每年的BNU校赛都会有两次赛前培训,为此就需要去借教室,由于SK同学忙于出题,这个事情就由小Q同学来跑腿。SK同学准备从宿舍出发,把借教室的单子交给小Q同学让他拿去教务处盖章,但是何老师突然发现SK同学好像借错教室了,想抢在借教室的单子被送到教务处之前拦截下来。

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

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

    Input

    第一行是一个正整数T(\leq 5),表示测试数据的组数,

    对于每组测试数据,

    第一行是两个整数n,q(1\leq n,q \leq 100000),分别表示节点数和询问数,

    接下来n-1行,每行包含两个整数x,y(1\leq x,y \leq n),表示xy之间有一条边相连,保证这些边能构成一棵树,

    接下来q行,每行包含三个整数A,B,C(1 \leq A,B,C \leq n),表示一个询问,其中A是何老师所在位置,B是SK同学所在位置,C是小Q同学所在位置,保证小Q同学初始不在教务处。

    Output

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

    Sample Input

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

    Sample Output

    YES
    NO

    Source

    Author

    SK


    题目大意:

    给你一棵树跟节点是1,q次询问,,每次循环给出 三个数 ABC 现在有一个人从B出发到达C再从C出发到达A,另一个人从A出发来拦截他,A,B同时出发,,问能否可以拦截B,当同时到达1时算拦截失败


    题目思路:


    很好想到的一个是分别求出从B->C->1的路程bc 和 A->1 的路程a  如果 bc<a则一定拦截不了 如果bc>a则一定可以拦截到,,如果bc=a 如果最后从C出发到达1和A不在同一颗子树上则拦截失败,也就是lca(A,C)==1时拦截失败,否则在到达1之前A可以拦截上,,对于求距离我们可以用lca来求


    AC代码:


    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 4e5;
    struct nod
    {
        int v,w;
        nod(int v,int w):v(v),w(w){}
        nod(){}
    };
    vector<nod>G[maxn];
    int val[maxn],R[maxn],first[maxn],vis[maxn];
    int dp[maxn][20];
    int n,m,q,tot;
    void dfs(int u,int dep)         //将树处理成线性的即每两个节点位于一段连续的区间里
    {
        val[++tot] = u,first[u] =tot,R[tot] = dep;
        vis[u] = 1;
        for(int i=0;i<G[u].size();i++)
        {
            int v = G[u][i].v,w = G[u][i].w;
            if(!vis[v])
            {
                dfs(v,dep+1);
                val[++tot] = u,R[tot] = dep;
            }
        }
    }
    void ST()             //ST预处理
    {
        for(int i=1;i<=tot;i++)
            dp[i][0] = i;
        for(int j=1;j<=log(tot)/log(2);j++)
            for(int i=1;i<=tot;i++)
                if(i+(1<<j)-1<=tot)
                {
                    int a = dp[i][j-1],b = dp[i+(1<<(j-1))][j-1];
                    dp[i][j] = (R[a]<R[b]?a:b);             //保存编号
                }
    }
    int RMQ(int l,int r)    //求编号
    {
        int k=0;
        while((1<<(k+1))<=r-l+1)
            k++;
        int a = dp[l][k],b = dp[r-(1<<k)+1][k];
        return (R[a]<R[b]?a:b);
    }
    int lca(int u,int v)   //求lca
    {
        int x = first[u],y = first[v];
        if(x>y)swap(x,y);
        int res = RMQ(x,y);
        return val[res];    //返回lca
    }
    int main()
    {
        int t;cin>>t;
        while(t--)
        {
            scanf("%d%d",&n,&q);
            tot = 0;
            for(int i=1;i<=n;i++)
                G[i].clear(),vis[i] = 0;
            for(int i=1;i<n;i++)
            {
                int x,y,z;scanf("%d%d",&x,&y);
                G[x].push_back(nod(y,1));
                G[y].push_back(nod(x,1));
            }
            dfs(1,1);
            ST();
            while(q--)
            {
                int a,b,c;scanf("%d%d%d",&a,&b,&c);
                int lbc = lca(b,c);
                int lac = lca(a,c);
                int len1 = R[first[b]]+R[first[c]]*2-R[first[lbc]]*2-R[1];
                int len2 = R[first[a]] - R[1];
                if(len1<len2||len1==len2&&lac==1)printf("NO\n");
                else printf("YES\n");
            }
        }
        return 0;
    }







    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值