其他分享
首页 > 其他分享> > POI2012 Rendezvous 基环树+分类讨论

POI2012 Rendezvous 基环树+分类讨论

作者:互联网

POI2012 Rendezvous

题目传送

sol:

首先把连通块划分出来。

对于不在一个连通块的两点不能相会,否则必定能相会。

在一个连通块内的又需分情况考虑。

先把环给拎出来,则环上每个点挂着一棵子树(不算环上的点)。

如果两点在一棵子树,则直接求lca即可,路径唯一,二者步数也唯一。

如果两点不在一棵子树,则必然二者都需要先走到环上,

然后两点有两种方式可以相会,按照题目要求讨论即可。

细节有点多,仔细考虑清楚每一步该怎么写就好了。

尽量减少vector的直接调用,不然不开O2的情况下,常数巨大!

#include<bits/stdc++.h>
#define IL inline
#define RG register
#define DB double
#define LL long long
#define pb push_back
using namespace std;

IL int gi() {
    RG int x=0,p=1; RG char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    return x*p;
}

const int N=5e5+3;

vector<int> S[N],cir[N];
int n,m,num,top,tot,a[N],in[N],id[N],cnt[N],vis[N],pos[N],dep[N],bel[N],sta[N],ins[N],head[N],f[N][20];

struct path{int a,b;};
struct EDGE{int next,to;}e[N<<1];

IL void make(int a,int b) {e[++tot]=(EDGE){head[a],b},head[a]=tot;}

IL void New_Graph() {
    tot=0;
    memset(&e,0,sizeof(e));
    memset(head,0,sizeof(head));
}

void Dfs(int x) {
    RG int i,y;
    vis[x]=1,pos[x]=num,S[num].pb(x);
    for(i=head[x];i;i=e[i].next)
        if(!vis[y=e[i].to]) Dfs(y);
}

void dfs(int x) {
    RG int i,y;
    for(i=head[x];i;i=e[i].next)
        if(!in[y=e[i].to])
            bel[y]=bel[x],f[y][0]=x,dep[y]=dep[x]+1,dfs(y);
}

IL void multiply(int Id) {
    RG int i,j,x;
    for(i=1;i<20;++i)
        for(j=0;j<S[Id].size();++j)
            x=S[Id][j],f[x][i]=f[f[x][i-1]][i-1];
}

IL path lca(int x,int y) {
    RG int i,fl=0,sx=0,sy=0;
    if(dep[x]<dep[y]) swap(x,y),fl=1;
    for(i=19;i>=0;--i)
        if(dep[f[x][i]]>=dep[y]) sx+=1<<i,x=f[x][i];
    if(x==y) return fl?(path){sy,sx}:(path){sx,sy};
    for(i=19;i>=0;--i)
        if(f[x][i]!=f[y][i]) sx+=1<<i,sy+=1<<i,x=f[x][i],y=f[y][i];
    return fl?(path){sy+1,sx+1}:(path){sx+1,sy+1};
}

void getcir(int x,int Id) {
    if(ins[x]) {
        RG int k;
        do{k=sta[top--],cir[Id].pb(k),in[k]=1,id[k]=++cnt[Id];}while(k!=x);
        return;
    }   
    sta[++top]=x,ins[x]=1,getcir(a[x],Id);
}

IL void getfore(int Id) {
    RG int i,x;
    for(i=0;i<cnt[Id];++i) 
        x=cir[Id][i],bel[x]=x,dep[x]=1,dfs(x);
    multiply(Id);
}

int main()
{
    RG int i,x,y;
    n=gi(),m=gi();
    for(i=1;i<=n;++i) a[i]=gi(),make(i,a[i]),make(a[i],i);
    for(i=1;i<=n;++i)
        if(!vis[i]) ++num,Dfs(i);
    for(i=1,New_Graph();i<=n;++i) make(a[i],i);
    for(i=1;i<=num;++i)
        top=0,getcir(S[i][0],i),getfore(i);
    while(m--) {
        x=gi(),y=gi();
        if(pos[x]!=pos[y]) {puts("-1 -1");continue;}
        else {
            if(bel[x]==bel[y]) {
                path s=lca(x,y);
                printf("%d %d\n",s.a,s.b);
            }
            else {
                int dx=bel[x],dy=bel[y],lx1,lx2,ly1,ly2,Mx1,Mx2,Mi1,Mi2;
                ly1=dep[y]-1,lx2=dep[x]-1;
                if(id[dx]<id[dy]) lx1=dep[x]-1+cnt[pos[x]]-id[dy]+id[dx],ly2=dep[y]-1+id[dy]-id[dx];
                else lx1=dep[x]-1+id[dx]-id[dy],ly2=dep[y]-1+cnt[pos[x]]+id[dy]-id[dx];
                Mx1=max(lx1,ly1),Mx2=max(lx2,ly2);
                if(Mx1==Mx2) {
                    Mi1=min(lx1,ly1),Mi2=min(lx2,ly2);
                    if(Mi1==Mi2) printf("%d %d\n",Mx1,Mi1);
                    else if(Mi1<Mi2) printf("%d %d\n",lx1,ly1);
                    else printf("%d %d\n",lx2,ly2);
                }
                else if(Mx1<Mx2) printf("%d %d\n",lx1,ly1);
                else printf("%d %d\n",lx2,ly2);                 
            }
        }
    }
    return 0;
}

标签:dep,ch,环上,POI2012,基环树,int,RG,Rendezvous,define
来源: https://www.cnblogs.com/Bhllx/p/11253310.html