其他分享
首页 > 其他分享> > 约会 Rendezvous:基环树/倍增lca

约会 Rendezvous:基环树/倍增lca

作者:互联网

提炼:tarjan判环,dfs建树,倍增lca,预处理环两点间距离

我犯的错误:

1.基环树不只有一棵,可以有很多

2.自环不能将其忽略,(对于我的算法)应该将其特殊考虑在算法内

3.代码一定要简洁有力,不能让自己调都恶心

Code

数组含义:

que[N]队列,in_que[N]tarjan判是否入队,dfn[N],low[N],bel[N]点属于的集合

ver[N]根节点的集合编号,to[N],nxt[N],head[N],f[N][20]倍增lca,d[N]深度,bel_rt[N]点属于的树的根

extra[N],Ex[N]特判自环专用,vector<int>vec[N]tarjan点集合

#include<cstdio>
#include<vector>
const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
using namespace std;
const int N=5e5+5;
int n,k,num_tarjan,num_bian,num_huan,top,
que[N],in_que[N],dfn[N],low[N],bel[N],ver[N],to[N],nxt[N],head[N],f[N][20],d[N],bel_rt[N],extra[N],Ex[N];
vector<int>vec[N];
int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
void add(int x,int y){if(x==y)extra[++extra[0]]=x;
    to[++num_bian]=y,nxt[num_bian]=head[x],head[x]=num_bian;
}
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a>b?b:a;}
void tarjan(int x){
    dfn[x]=low[x]=++num_tarjan;
    que[++top]=x;in_que[x]=1;
    for(int i=head[x],y;i;i=nxt[i])
        if(!dfn[y=to[i]])tarjan(y),low[x]=min(low[x],low[y]);
        else if(in_que[y])low[x]=min(low[x],dfn[y]);
    if(dfn[x]==low[x]){
        num_huan++;
        int y;
        do{
            y=que[top--];
            in_que[y]=0;
            vec[num_huan].push_back(y);
            bel[y]=num_huan;
        }while(y!=x);
        if((int)vec[num_huan].size()>1)Ex[++Ex[0]]=num_huan;
    }
}
void dfs(int x,int depth,int root){
    bel_rt[x]=root;
    d[x]=depth;
    for(int i=head[x],y;i;i=nxt[i]){
        if(bel[root]!=bel[y=to[i]]&&!d[y=to[i]]){
            f[y][0]=x;
            for(int j=1;j<=19;++j)f[y][j]=f[f[y][j-1]][j-1];
            dfs(y,depth+1,root);
        }
    }
}
int get(int x,int y){
    if(d[x]<d[y])swap(x,y);
    for(int i=19;i>=0;--i)if(d[f[x][i]]>=d[y])x=f[x][i];
    if(x==y)return y;
    for(int i=19;i>=0;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[y][0];
}
int main(){
    n=read(),k=read();
    for(int i=1;i<=n;++i)add(read(),i);
    for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    for(int hua=1;hua<=Ex[0];++hua){int huan=Ex[hua];
        for(int i=0;i<(int)vec[huan].size();++i){
            dfs(vec[huan][i],1,vec[huan][i]);
            ver[vec[huan][i]]=i+1;
        }
    }
    for(int i=1;i<=extra[0];++i)
        if(!d[extra[i]]){
            dfs(extra[i],1,extra[i]);
            ver[extra[i]]=1;
        }
    for(int i=1,x,y;i<=k;++i){
        x=read(),y=read();
        if(bel[bel_rt[x]]!=bel[bel_rt[y]])
            printf("-1 -1\n");
        else if(bel_rt[x]==bel_rt[y]){
            int lca=get(x,y);
            printf("%d %d\n",d[x]-d[lca],d[y]-d[lca]);
        }
        else{
            int ai=d[x]-1,bi=d[y]-1,huan_dis=(int)vec[bel[bel_rt[x]]].size();
            int len_b=ver[bel_rt[x]]-ver[bel_rt[y]],len_a;
            if(len_b<0)len_a=-1*len_b,len_b=huan_dis-len_a;
            else len_a=huan_dis-len_b;
            int Ma=max(ai+len_a,bi),Mb=max(ai,bi+len_b),ma=min(ai+len_a,bi),mb=min(ai,bi+len_b);
            //条件一
            if(Ma<Mb)
                printf("%d %d\n",ai+len_a,bi);
            else if(Ma>Mb)
                printf("%d %d\n",ai,bi+len_b);
            else{
                //条件二
                if(ma<mb)
                    printf("%d %d\n",ai+len_a,bi);
                else if(ma>mb)
                    printf("%d %d\n",ai,bi+len_b);
                else{
                    //条件三
                    if(ai+len_a>=bi)
                        printf("%d %d\n",ai+len_a,bi);
                    else
                        printf("-1 -1\n");
                }
            }
        }
        
        }
    return 0;
}

 

标签:tarjan,bel,int,lca,基环树,num,que,low,Rendezvous
来源: https://www.cnblogs.com/hzoi2018-xuefeng/p/11175296.html