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; }