• [Poi2012]Rendezvous


    题目描述

    给定一个n个顶点的有向图,每个顶点有且仅有一条出边。

    对于顶点i,记它的出边为(i, a[i])。

    再给出q组询问,每组询问由两个顶点a、b组成,要求输出满足下面条件的x、y:

    1. 从顶点a沿着出边走x步和从顶点b沿着出边走y步后到达的顶点相同。

    2. 在满足条件1的情况下max(x,y)最小。

    3. 在满足条件1和2的情况下min(x,y)最小。

    4. 在满足条件1、2和3的情况下x>=y。如果不存在满足条件1的x、y,输出-1 -1。

    输入格式

    第一行两个正整数n和q (n,q<=500,000)。

    第二行n个正整数a[1],a[2],...,a[n] (a[i]<=n)。

    下面q行,每行两个正整数a,b (a,b<=n),表示一组询问。

    输出格式

    输出q行,每行两个整数。


    第一句话就明显地点明了这是一道基环树的题。并且还是一颗内向基环树。

    我们考虑每对x,y的情况:

    1.x,y不在一棵基环树上。那么输出-1即可。

    2.x,y在同一棵子树上。那么答案就是树上距离,用lca求解即可。

    3.x,y在同一棵基环树上,但不在同一棵子树上。由于是有向图,我们可以枚举x走过环到y的距离和y走过环到x的距离,根据题目输出答案即可。

    具体流程:

    1.首先预处理出环。

    2.预处理出树上倍增的fa数组和每个点所在的子树、基环树编号,并且准备好要使用的距离的信息。

    对于每个询问,在线回答即可。

    时间复杂度为O(N+Q) ~ O((N+Q)logN)

    *很容易写爆的基环树的题。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define maxn 500010
    #define maxm 500010
    using namespace std;
     
    struct graph{
        struct edge{
            int to,next;
            edge(){}
            edge(const int &_to,const int &_next){ to=_to,next=_next; }
        }e[maxn<<1];
        int head[maxn],k;
        inline void init(){ memset(head,-1,sizeof head); }
        inline void add(const int &u,const int &v){ e[k]=edge(v,head[u]),head[u]=k++; }
    }a,b;
     
    int id[maxn],stack[maxn],top,col[maxn],cnt,size[maxn];
    int fa[maxn][20],root[maxn],dep[maxn];
    bool inloop[maxn],vis[maxn],instack[maxn];
    int n,m,maxdep;
     
    inline int read(){
        register int x(0),f(1); register char c(getchar());
        while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
        while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
     
    void dfs_getloop(int u){
        stack[++top]=u,instack[u]=vis[u]=true;
        for(register int i=a.head[u];~i;i=a.e[i].next){
            int v=a.e[i].to;
            if(!vis[v]) dfs_getloop(v);
            else if(instack[v]){
                int w,t=top; cnt++;
                do{ w=stack[t--],instack[w]=false,inloop[w]=true,col[w]=cnt,id[w]=++size[cnt]; }while(w!=v);
            }
        }
        instack[u]=false,top--;
    }
    void dfs_getfa(int u,int rt){
        root[u]=rt,col[u]=col[rt];
        for(register int i=b.head[u];~i;i=b.e[i].next){
            int v=b.e[i].to;
            if(inloop[v]||root[v]) continue;
            dep[v]=dep[u]+1,fa[v][0]=u;
            for(register int i=1;i<=maxdep;i++) fa[v][i]=fa[fa[v][i-1]][i-1];
            dfs_getfa(v,rt);
        }
    }
     
    inline int lca(int u,int v){
        if(dep[u]>dep[v]) swap(u,v);
        for(register int i=maxdep;i>=0;i--) if(dep[fa[v][i]]>=dep[u]) v=fa[v][i];
        if(u==v) return u;
        for(register int i=maxdep;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
        return fa[u][0];
    }
     
    inline bool cmp(int x1,int y1,int x2,int y2){
        if(max(x1,y1)!=max(x2,y2)) return max(x1,y1)<max(x2,y2);
        if(min(x1,y1)!=min(x2,y2)) return min(x1,y1)<min(x2,y2);
        return x1>=y1;
    }
     
    int main(){
        a.init(),b.init();
        n=read(),m=read();
        for(register int i=1;i<=n;i++){
            int v=read();
            a.add(i,v),b.add(v,i);
        }
     
        maxdep=(int)log(n)/log(2)+1;
        for(register int i=1;i<=n;i++) if(!vis[i]) dfs_getloop(i);
        for(register int i=1;i<=n;i++) if(inloop[i]) dfs_getfa(i,i);
        for(register int i=1;i<=m;i++){
            int u=read(),v=read();
            if(col[u]!=col[v]){ puts("-1 -1"); continue; }
            if(root[u]==root[v]){
                int w=lca(u,v);
                printf("%d %d
    ",dep[u]-dep[w],dep[v]-dep[w]);
            }else{
                int ans1=dep[u],ans2=dep[v];
                int dis1=(id[root[u]]-id[root[v]]+size[col[u]])%size[col[u]];
                int dis2=(id[root[v]]-id[root[u]]+size[col[v]])%size[col[v]];
                if(cmp(ans1+dis1,ans2,ans1,ans2+dis2)) printf("%d %d
    ",ans1+dis1,ans2);
                else printf("%d %d
    ",ans1,ans2+dis2);
            }
        }
        return 0;
    }
    

    附上我用来调试的代码(太容易写爆了)

        //for(register int i=1;i<=n;i++) if(inloop[i]) printf("%d ",i);puts("");
        //for(register int i=1;i<=n;i++)  printf("%d ",id[i]);puts("");
        //for(register int i=1;i<=n;i++) printf("%d ",col[i]);puts("");
        //for(register int i=1;i<=n;i++) printf("%d ",size[col[i]]);puts("");
        //for(register int i=1;i<=n;i++) printf("%d ",root[i]);puts("");
        //for(register int i=1;i<=n;i++) printf("%d ",dep[i]);puts("");
        //for(register int i=1;i<=n;i++) printf("%d ",inloop[i]);puts("");
        /*for(register int i=1;i<=n;i++){
            for(register int j=0;j<=maxdep;j++) printf("%d ",fa[i][j]);puts("");
        }*/
        //printf("%d
    ",maxdep);
    
  • 相关阅读:
    19 C#循环语句的跳过和中断 continue和break
    18 C#中的循环执行 for循环
    一种绝对提高开发水平的方法(推荐英语)
    大数据知识普及
    全链路压测压测报告
    QuickSearch快排
    二分查找
    算法
    基于领域驱动设计的业务中台架构设计
    【科普】Scrum——从橄榄球争球到敏捷开发
  • 原文地址:https://www.cnblogs.com/akura/p/10920021.html
Copyright © 2020-2023  润新知