版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/82149273
Description
给定一个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。
Input
第一行两个正整数n和q (n,q<=500,000)。 第二行n个正整数a[1],a[2],…,a[n] (a[i]<=n)。
下面q行,每行两个正整数a,b (a,b<=n),表示一组询问。
Output
输出q行,每行两个整数。
Sample Input
12 5
4 3 5 5 1 1 12 12 9 9 7 1
7 2
8 11
1 2
9 10
10 5
Sample Output
2 3
1 2
2 2
0 1
-1 -1
题解
发现这是一棵基环内向树
找到环把他缩成一个点,作为该树的根
然后预处理树上倍增
询问的时候先看看lca是否是环缩成的点,不是就直接输出
是的话,我们找到这两个点到环上对应的点。计算一下两条路径分别的贡献,取最小输出
队友说很卡常我就写了一堆优化233
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline int write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
int ff[500005],rd[500005][2];
int findff(int x){return ff[x]==x?ff[x]:ff[x]=findff(ff[x]);}
struct node{int x,y,next;}a[500005],b[500005];int len1,last1[500005],len2,last2[1000005];
void ins_a(int x,int y){len1++;a[len1].x=x;a[len1].y=y;a[len1].next=last1[x];last1[x]=len1;}
void ins_b(int x,int y){len2++;b[len2].x=x;b[len2].y=y;b[len2].next=last2[x];last2[x]=len2;}
int is_ring[500005],nd,n,m;
int dis[500005],mx[1000005];
bool findring(int x,int ed)
{
if(x==ed){is_ring[x]=nd;return true;}
for(int k=last1[x];k;k=a[k].next)
if(findring(a[k].y,ed))
{
is_ring[x]=nd;dis[x]=dis[a[k].y]+1;
mx[nd]=max(mx[nd],dis[x]);
return true;
}
return false;
}
int vis[500005];
void newnode(int x,int nd)
{
for(int k=last1[x];k;k=a[k].next)
{
int y=a[k].y;if(vis[y])continue;
vis[y]=1;
if(is_ring[y])newnode(y,nd);
else ins_b(nd,y),newnode(y,y);
}
}
int fa[700005][20],dep[700005],bin[20];
void pre_tree_node(int x)
{
for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int k=last2[x];k;k=b[k].next)
{
int y=b[k].y;
if(y!=fa[x][0])
{
fa[y][0]=x;dep[y]=dep[x]+1;
pre_tree_node(y);
}
}
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(bin[i]<=dep[x] && dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(bin[i]<=dep[x] && fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline int up(int x,int po)
{
for(int i=19;i>=0;i--)if(bin[i]<=po)x=fa[x][i],po-=bin[i];
return x;
}
int nxt[500005];
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
bin[0]=1;for(int i=1;i<=19;i++)bin[i]=bin[i-1]<<1;
n=read();m=read();
for(int i=1;i<=n;i++)ff[i]=i;
for(int i=1;i<=n;i++)
{
nxt[i]=read();
int x=nxt[i];
int p=findff(x),q=findff(i);
if(p!=q)ff[p]=q,rd[q][0]=rd[p][0],rd[q][1]=rd[p][1],ins_a(x,i);
else rd[p][0]=x,rd[p][1]=i;
}
nd=n;
for(int i=1;i<=n;i++)
if(ff[i]==i)
{
nd++;
findring(rd[i][1],rd[i][0]);
newnode(rd[i][1],nd);
pre_tree_node(nd);mx[nd]++;
}
// for(int i=1;i<=len2;i++)printf("CHECKER:%d %d\n",b[i].x,b[i].y);
while(m--)
{
int x=read(),y=read();
if(findff(x)!=findff(y)){printf("-1 -1\n");continue;}
int u=x,v=y;
if(is_ring[x])u=is_ring[x];
if(is_ring[y])v=is_ring[y];
int LA=lca(u,v);
if(LA<=n)printf("%d %d\n",dep[x]-dep[LA],dep[y]-dep[LA]);
else
{
int ax=dep[x],ay=dep[y];
int p=up(x,dep[x]-1),q=up(y,dep[y]-1);
u=nxt[p],v=nxt[q];//环上的点
if(is_ring[x])u=x;
if(is_ring[y])v=y;
int gg=is_ring[u];//环的编号
if(dis[u]>dis[v])//环的方向 u在v的后面
{
int s1=ax,s2=ay+dis[u]-dis[v];
int s3=mx[gg]-(dis[u]-dis[v])+ax,s4=ay;
if(max(s1,s2)<max(s3,s4))
{
write(s1);printf(" ");write(s2);
}
else if(max(s1,s2)>max(s3,s4))
{
write(s3);printf(" ");write(s4);
}
else if(min(s1,s2)<min(s3,s4))
{
write(s1);printf(" ");write(s2);
}
else if(min(s1,s2)>min(s3,s4))
{
write(s3);printf(" ");write(s4);
}
else if(s1>=s2)
{
write(s1);printf(" ");write(s2);
}
else
{
write(s3);printf(" ");write(s4);
}
}
else//v在u后面
{
int s1=ax+dis[v]-dis[u],s2=ay;
int s3=ax,s4=ay+mx[gg]-(dis[v]-dis[u]);
if(max(s1,s2)<max(s3,s4))
{
write(s1);printf(" ");write(s2);
}
else if(max(s1,s2)>max(s3,s4))
{
write(s3);printf(" ");write(s4);
}
else if(min(s1,s2)<min(s3,s4))
{
write(s1);printf(" ");write(s2);
}
else if(min(s1,s2)>min(s3,s4))
{
write(s3);printf(" ");write(s4);
}
else if(s1>=s2)
{
write(s1);printf(" ");write(s2);
}
else
{
write(s3);printf(" ");write(s4);
}
}
puts("");
}
}
return 0;
}