http://acm.hdu.edu.cn/showproblem.php?pid=4297
This title is a special FIG forest trees special in that it has a ring this ring node comprises a root
The basic idea:
First with disjoint-set processing of FIG.
1 establish a complete forest
2 sides that constitute the ring without the addition of forest
3 has the number of points recorded in each ring belonging to several points on each ring ring
Then deal with couples
Not a tree unreachable
With a special treatment in the room
The couples Storage
Here LCA LCA LCA and different from the original
If the most recent common ancestor does not ring if obtained directly seek it in the ring which are over a point on the ring
Finally, like processed
Selecting the optimum different schemes
Codes and their comments:
#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;
}
Reproduced in: https: //www.cnblogs.com/liulangye/archive/2012/09/19/2694305.html