这是一个基环内向树森林。两个点如果在一个子树中,直接倍增lca。
否则两个点先走到环上,然后一个点不动,另一个点走到这个点。
#include <bits/stdc++.h>
using namespace std;
#define N 510000
int n,q,top,cnt;
int fa[N][21];
int vis[N],st[N],inc[N],root[N],deep[N];
int bel[N],ins[N],num[N],sum[N];
void dfs(int x)
{
st[++top]=x;vis[x]=ins[x]=1;
int y=fa[x][0];
if(ins[y])
{
int t=0;
bel[y]=++cnt;inc[y]=1;num[y]=t;
fa[y][0]=0;root[y]=y;
for(int i=top;st[i]!=y;i--)
{
inc[st[i]]=1,bel[st[i]]=cnt;
root[st[i]]=st[i];num[st[i]]=++t;
fa[st[i]][0]=0;
}
sum[cnt]=t+1;ins[x]=0;
return;
}
if(!vis[y])dfs(y);
ins[x]=0;
if(inc[x])return;
bel[x]=bel[y];deep[x]=deep[y]+1;
root[x]=root[y];
for(int i=1;i<=20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
}
int lca(int x,int y)
{
if(deep[x]<deep[y])swap(x,y);
for(int i=20;i>=0;i--)
if(deep[fa[x][i]]>=deep[y])
x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&fa[i][0]);
for(int i=1;i<=n;i++)
if(!vis[i]){top=0;dfs(i);}
for(int a,b,x,y;q--;)
{
scanf("%d%d",&a,&b);
if(bel[a]!=bel[b])
{puts("-1 -1");continue;}
if(root[a]==root[b])
{
int t=lca(a,b);
x=deep[a]-deep[t],y=deep[b]-deep[t];
printf("%d %d\n",x,y);
}
else
{
x=deep[a];y=deep[b];
a=root[a];b=root[b];
int m=sum[bel[a]];
int x1=x+(num[a]-num[b]+m)%m,y1=y;
int x2=x,y2=y+(num[b]-num[a]+m)%m;
int mx1=max(x1,y1),mx2=max(x2,y2),mn1=min(x1,y1),mn2=min(x2,y2);
if(mx1<mx2||(mx1==mx2&&mn1<mn2)||(mx1==mx2&&mn1==mn2&&x1>=y1))
printf("%d %d\n",x1,y1);
else printf("%d %d\n",x2,y2);
}
}
return 0;
}