hdu 4297 One and One Story

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;

}



 

你可能感兴趣的:(HDU)