http://acm.hdu.edu.cn/showproblem.php?pid=4297

此题目中的图是一个特殊的森林  特殊在于它的树都有一个环 这个环内点包括树根

基本思路:

先用并查集 对图进行处理

1  建立完整森林

2  构成环的边不加入森林

3  记录每个环上有多少点  每个环上点属于第几个环

然后处理 couples

不在一个树上的不可达

同一个 room 内 特别处理

将 couples 储存

然后LCA  这里LCA 与原始LCA 有不同

如果最近公共祖先不在环上 直接求出 如果在环上 求其分别是环上哪一个点过来的

最后就好处理了

对不同方案进行选优

代码及其注释:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>

#define LL long long
//外挂开栈
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int N=500005;
vector<int>qtnum[N];//表示有此点 的couple 是第几个couple
vector<int>treehead;//每个数的树根
vector<int>cirnodenum;//一个树对应一个环 这个环的点个数
struct node1{
    int l,r;
    int lf,rf;
}qt[N];//couples l,r 如果公共最近父节点不在环上 lf,rf则相等 
      //否则对应表示从环上哪个点过来
int head[N];
struct node
{
    int j,next;
}side[N];//邻接表存森林
int I;
int f[N];//并查集使用
int pre[N];//每个点对应的前驱结点
int cir[N],T;//每个点对应第几个环 不在环上为 -1 T用来环计数
int dist[N];//每个点到树根的距离
int Findf(int x)
{
    if(x!=f[x])
    f[x]=Findf(f[x]);
    return f[x];
}
void Build(int i,int j)
{
    side[I].j=j;
    side[I].next=head[i];
    head[i]=I++;
}
void Findcir(int boot,int k)//求环内点个数 及环上点标记在第几个环上
{
    int sum=0;
    while(k!=boot){
        ++sum;
        cir[k]=T;
        k=pre[k];
    }
    ++sum;
    cirnodenum.push_back(sum);
    cir[k]=T++;
}
void Searchqt(int l,int k)//找 couples 的lf 和 rf
{
    for(unsigned int i=0;i<qtnum[l].size();++i)
    {
        int j=qtnum[l][i];
        if(l==qt[j].l)
        {
            if(f[qt[j].r]!=-1)
            {
                int ftemp=Findf(qt[j].r);
                if(cir[ftemp]!=-1)qt[j].lf=k;
                else qt[j].lf=ftemp;
                qt[j].rf=ftemp;
            }
        }else
        {
            if(f[qt[j].l]!=-1)
            {
                int ftemp=Findf(qt[j].l);
                if(cir[ftemp]!=-1)qt[j].rf=k;
                else qt[j].rf=ftemp;
                qt[j].lf=ftemp;
            }
        }
    }
}
void dfs(int x,int pre,int k,int d)//LCA
{
    dist[x]=d;
    if(cir[x]!=-1)//从环上哪个点上过来 如果此点在环上 更新
    k=x;
    Searchqt(x,k);
    f[x]=x;
    for(int t=head[x];t!=-1;t=side[t].next)
    {
        int l=side[t].j;
        dfs(l,x,k,d+1);
    }
    if(cir[x]==-1)//环上的点不在想前指向
    f[x]=pre;
}
void Lca()
{
    memset(f,-1,sizeof(f));
    for(unsigned int i=0;i<treehead.size();++i)
    dfs(treehead[i],treehead[i],-1,0);
}
int main()
{
    //freopen("data.txt","r",stdin);
    int n,K;
    while(scanf("%d %d",&n,&K)!=EOF)
    {
        memset(head,-1,sizeof(head));
        memset(cir,-1,sizeof(cir));
        I=0;T=0;
        for(int i=1;i<=n;++i)
        {f[i]=i;qtnum[i].clear();}
        treehead.clear();cirnodenum.clear();
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&pre[i]);
            if(Findf(i)!=Findf(pre[i]))//是否有环
            {
                f[Findf(i)]=Findf(pre[i]);
                Build(pre[i],i);
            }
            else
            {
                Findcir(i,pre[i]);//有环的话 就记录相关信息
                treehead.push_back(i);
            }
        }
        for(int i=0;i<K;++i)//对couples 进行取舍 处理记录
        {
            scanf("%d %d",&qt[i].l,&qt[i].r);
            if(Findf(qt[i].l)!=Findf(qt[i].r))
            {qt[i].lf=qt[i].rf=-1;continue;}
            if(qt[i].l==qt[i].r)
            {qt[i].lf=qt[i].rf=0;continue;}
            if(cir[qt[i].l]!=-1&&cir[qt[i].r]!=-1)
            {qt[i].lf=qt[i].l;qt[i].rf=qt[i].r;continue;}
            qtnum[qt[i].l].push_back(i);
            qtnum[qt[i].r].push_back(i);
        }
        Lca();
        int ansa,ansb,A,B,atemp,btemp;
        int maxa,maxb,mina,minb;
        for(int i=0;i<K;++i)//找各种方案最优
        {
            if(qt[i].lf<=0)
            {printf("%d %d\n",qt[i].lf,qt[i].rf);continue;}
            A=dist[qt[i].l]-dist[qt[i].lf];
            B=dist[qt[i].r]-dist[qt[i].rf];
            if(qt[i].lf==qt[i].rf)
            {printf("%d %d\n",A,B);continue;}
            atemp=dist[qt[i].lf]-dist[qt[i].rf];
            if(atemp<0)atemp+=cirnodenum[cir[qt[i].lf]];
            btemp=dist[qt[i].rf]-dist[qt[i].lf];
            if(btemp<0)btemp+=cirnodenum[cir[qt[i].rf]];
            maxa=max(A+atemp,B);
            maxb=max(A,B+btemp);
            mina=min(A+atemp,B);
            minb=min(A,B+btemp);
            if(maxa<maxb||(maxa==maxb&&(mina<minb||(mina==minb&&A+atemp>=B))))
            {ansa=A+atemp;ansb=B;}
            else
            {ansa=A;ansb=B+btemp;}
            printf("%d %d\n",ansa,ansb);
        }
    }
    return 0;
}

 

posted on 2012-09-19 20:49  夜->  阅读(246)  评论(0编辑  收藏  举报