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;
}
转载于:https://www.cnblogs.com/liulangye/archive/2012/09/19/2694305.html