Gym 100685G Gadget Hackwrench (LCA)

来源:互联网 发布:苹果打击垫软件 编辑:程序博客网 时间:2024/05/22 13:16

【题意】给了一颗V<=100000的有向的,但是忽略方向的一颗树,每次查询(u,v)是否可以到达?

【解题方法】随便一个点当成根,那么边有两种,一种是向上一种向下。我们可以预处理dp[u]表示u向上或者下的边的条数,有了这个就可以快速算出u向上或者向下的边的条数。当询问(u,v)是否可以到达的时候,只需要判断u->lca(u,v)lca(u,v)->v路径上的上下边是否相同就可以了。

【AC 代码】

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 100010;int n,q;struct edge{int v;bool flag;};vector<edge>G[maxn];int dep[maxn],dp[maxn],p[18][maxn];void dfs(int u,int f,int d){    dep[u]=d;p[0][u]=f;    for(int i=0; i<G[u].size(); i++){        edge e=G[u][i];        if(e.v==f) continue;        dp[e.v]=dp[u]+(e.flag?1:-1);        dfs(e.v,u,d+1);    }}void build(){    dp[1]=0;    dfs(1,-1,0);    for(int i=0; i+1<18; i++){        for(int v=1; v<=n; v++){            if(p[i][v]<0) p[i+1][v]=-1;            else p[i+1][v]=p[i][p[i][v]];        }    }}int lca(int u,int v){    if(dep[u]>dep[v]) swap(u,v);    for(int i=0; i<18; i++){        if(dep[v]-dep[u]>>i&1) v=p[i][v];    }    if(u==v) return u;    for(int i=17; i>=0; i--){        if(p[i][u]!=p[i][v]){            u=p[i][u],v=p[i][v];        }    }    return p[0][u];}int main(){    int u,v;    while(scanf("%d",&n)!=EOF)    {        for(int i=1; i<=n; i++) G[i].clear();        for(int i=1; i<n; i++){            scanf("%d%d",&u,&v);            G[u].push_back(edge{v,1});            G[v].push_back(edge{u,0});        }        scanf("%d",&q);        build();        while(q--)        {            scanf("%d%d",&u,&v);            int _lca=lca(u,v);            int depu=dep[u]-dep[_lca];            int depv=dep[v]-dep[_lca];            int disu=dp[u]-dp[_lca];            int disv=dp[v]-dp[_lca];            if(depu==-disu&&depv==disv){                printf("Yes\n");            }            else{                printf("No\n");            }        }    }    return 0;}


0 0