[bzoj2791][基环树][树上倍增]Rendezvous

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/82149273