BZOJ2791: [Poi2012]Rendezvous

本文介绍了一种解决环套树结构问题的算法实现,通过LCA(最近公共祖先)算法结合乱搜等策略来求解特定路径上的节点距离问题。文章详细展示了算法的具体实现过程,包括节点查找、路径压缩、树形结构遍历等关键步骤。

这是很多个环套树,然后理解了题意lca+乱搜什么的…

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;

const int maxn = 510000;
const int maxb = 21;

struct edge
{
    int y,nex;
    edge(){}
    edge(int _y,int _nex){y=_y;nex=_nex;}
}a[maxn]; int len,fir[maxn];
void ins(int x,int y){a[++len]=edge(y,fir[x]); fir[x]=len;}
int n,q,_to[maxn];

int fa[maxn];
int find_(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find_(fa[x]);
}

int bel[maxn],id;

bool v[maxn];
int c_sum[maxn],c_id[maxn];
int sta[maxn],tp;
void f_c(int x)
{
    if(v[x])
    {
        int cid=0;
        int la=0;
        while(la!=x)
        {
            la=sta[tp--];
            c_sum[bel[x]]++;
            c_id[la]=++cid;
        }
        return ;
    }
    v[x]=true;
    sta[++tp]=x;
    f_c(_to[x]);
}

int tid[maxn],f[maxn][maxb],dep[maxn];
void dfs(int x,int rt)
{
    tid[x]=rt;
    for(int i=1;i<maxb;i++)
        f[x][i]=f[f[x][i-1]][i-1];
    for(int k=fir[x];k;k=a[k].nex)
    {
        int y=a[k].y;
        if(!c_id[y])
        {
            f[y][0]=x;
            dep[y]=dep[x]+1;
            dfs(y,rt);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=maxb-1;i>=0;i--)
        if(dep[x]-(1<<i)>=dep[y]) x=f[x][i];
    if(x==y) return x;

    for(int i=maxb-1;i>=0;i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main()
{
    memset(fir,0,sizeof fir); len=0;

    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        int x; scanf("%d",&x);
        ins(x,i); _to[i]=x;
        fa[find_(x)]=fa[find_(i)];
    }

    memset(bel,0,sizeof bel); id=0;
    for(int i=1;i<=n;i++) bel[i]=find_(i);

    memset(c_sum,0,sizeof c_sum);
    memset(v,false,sizeof v);
    for(int i=1;i<=n;i++)
        if(!c_sum[bel[i]]) tp=0,f_c(i);

    for(int i=1;i<=n;i++)
        if(c_id[i])
        {
            dep[i]=0;
            dfs(i,i);
        }

    while(q--)
    {
        int a1,a2;
        int x,y; scanf("%d%d",&x,&y);
        if(bel[x]!=bel[y]){ printf("-1 -1\n"); continue; }
        if(tid[x]!=tid[y])
        {
            a1=dep[x]; a2=dep[y];
            x=tid[x]; y=tid[y];
            int tmp=abs(c_id[x]-c_id[y]);
            int t1,t2;
            t1=c_id[x]>c_id[y]?c_sum[bel[x]]-tmp:tmp;
            t2=c_id[x]>c_id[y]?tmp:c_sum[bel[x]]-tmp;
            swap(t1,t2);
            if(t1&&t2)
            {
            if(a1+t1==a2+t2)
            {
                if(a1>=a2) a1+=t1;
                else a2+=t2;
            }
            else if(a1+t1>a2+t2) a2+=t2;
            else a1+=t1;
            }
            printf("%d %d\n",a1,a2);
        }
        else
        {
            int LCA=lca(x,y);
            a1=dep[x]-dep[LCA];
            a2=dep[y]-dep[LCA];
            printf("%d %d\n",a1,a2);
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值