【题解】Acwing392 会合

Acwing392 会合

\(\text{Solution:}\)

简单题……确实

这是一个基环树森林上找两点换上最短移动方案的题。慢慢考虑其性质。

  • 每个点有且仅有一条出边

这意味着这一定是 内向基环树森林

还有更重要的意义:环有顺序

  • 求两个点移动到一个公共点上

首先考虑两个点共同在一棵树内的情况,也就是它们两个不跨环。

这个时候我们发现,由于树有方向,所以它们只能向上跳,这也意味着答案就是 \(LCA\) 与它们深度的差。

那么两个点不在同一棵基环树中呢?直接输出 -1 -1 ,这也是唯一的无解情况。

那么,如果它们需要跨环,我们就先让他们跳到环上考虑。

引理:环上点移动最优情况下一定只会移动一个点。

证明:题目中要求了使 \((x,y)\) 的最大值最小值均最小,意味着我们不能做多余的移动。而两个点如果同时移动显然会无故增加次数,因为环是单向的。

那么剩下就是考虑如何计算并分类了。

关于计算环上的跳跃,考虑 按照顺序 来对每个点进行标号,这样就可以直接做差得到距离了。注意有两个距离,一个是正向直接跳,另一个是让另一个点绕一圈跳回来。

然后按照题目所说分类讨论即可。但是有一些情况需要注意:

  1. 环是有方向的,所以在找环的时候 必须 用有向图来找。

  2. 打标记需要全部打上,所以这个时候我们选择用一张无向图来做。

  3. 会出现自环什么的情况,但是自然做就不会错。

  4. 有向图找环不需要判父亲!!!!

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int head[N],tot,n,f[N][20],q,H[N],Tot;
struct E{int nxt,to;}e[N<<1],edge[N<<1];
inline int Max(int x,int y){
	return x>y?x:y;
}
inline int Min(int x,int y){
	return x<y?x:y;
} 
inline void link(int u,int v){
	e[++tot]=(E){head[u],v};
	head[u]=tot;
}
inline void Link(int u,int v){
	edge[++Tot]=(E){H[u],v};
	H[u]=Tot;
}
vector<int>Cir[N];
int num,st[N],top,dep[N],vis[N];
int to[N],Tg[N],trg[N],rk[N],Vis[N],ts[N]; 
void Get(int node,int x){
	int i;
	for(i=top;i&&st[i]!=x;--i)Cir[node].push_back(st[i]),st[i]=0;
	Cir[node].push_back(x);st[i]=0; 
}
void Find_Circle(int x,int fa,int node,int tg){
	vis[x]=1;st[++top]=x;Tg[x]=tg;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		
		if(vis[j]&&Cir[node].empty()){
			Get(node,j);
		}
		if(!vis[j])Find_Circle(j,x,node,tg);
	}
	st[top]=0;
	--top;
}
void Dtag(int x,int fa,int tg){
	Vis[x]=1;Tg[x]=tg;
	for(int i=H[x];i;i=edge[i].nxt){
		int j=edge[i].to;
		if(j==fa)continue;
		if(Vis[j])continue;
		Dtag(j,x,tg);
	}
}
void dfs(int x,int fa,int tg){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;trg[x]=tg;
	for(int i=1;i<20;++i)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=H[x];i;i=edge[i].nxt){
		int j=edge[i].to;
		if(j==fa)continue;
		if(vis[j])continue;
		dfs(j,x,tg);
	}
}
int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;~i;--i)if(f[x][i]&&dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=19;~i;--i)if(f[x][i]&&f[y][i]&&f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
// 	freopen("in.txt","r",stdin);
// 	freopen("392.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i){
		scanf("%d",&to[i]);
		link(i,to[i]);
//		link(to[i],i);?
		Link(i,to[i]);
		Link(to[i],i);
//		printf("(%d %d)\n",i,to[i]);
	}
	for(int i=1;i<=n;++i)
		if(!vis[i]&&!Vis[i]){
			top=0;
			num++;
			Find_Circle(i,0,num,num);
			Dtag(i,0,num);
			if(Cir[num].empty())Cir[num].push_back(i);
		}
	for(int i=1;i<=n;++i)vis[i]=0;
//	puts("Circle:");
//	for(int i=1;i<=num;++i){
//		printf("%d:\n",i);
//		for(auto v:Cir[i])printf("%d ",v);
//		puts("");
//	}
//	puts("End.");
	for(int i=1;i<=num;++i)
		for(auto v:Cir[i])
			vis[v]=1;
	for(int i=1;i<=num;++i)
		for(auto v:Cir[i])
			dfs(v,0,v);
	for(int i=1;i<=num;++i){
		reverse(Cir[i].begin(),Cir[i].end());
		int Cirsiz=Cir[i].size();
		for(int j=0;j<Cirsiz;++j){
			rk[Cir[i][j]]=j+1;
		}
	}
//	for(int i=1;i<=n;++i)printf("%d ",rk[i]);
//	puts("");
//	for(int i=1;i<=n;++i)printf("%d ",dep[i]);
//	puts("");
//	for(int i=1;i<=n;++i)printf("%d ",vis[i]);
//	puts("");
//	for(int i=1;i<=n;++i)printf("%d ",Tg[i]);
//	puts("");
//	for(int i=1;i<=n;++i)printf("%d ",trg[i]);
//	puts("");
	for(int qq=1;qq<=q;++qq){
		int a,b;
		scanf("%d%d",&a,&b);
		if(Tg[trg[a]]!=Tg[trg[b]]){
// 			printf("(%d %d)\n",Tg[a],Tg[b]);
// 			printf("(%d %d)\n",trg[a],trg[b]);
// 			printf("(%d %d)\n",Tg[trg[a]],Tg[trg[b]]);
			puts("-1 -1");
			continue;
		}
		if(trg[a]==trg[b]){
			int L=LCA(a,b);
			int xx=dep[a]-dep[L];
			int yy=dep[b]-dep[L];
			printf("%d %d\n",dep[a]-dep[L],dep[b]-dep[L]);
			assert(xx>=0);assert(yy>=0);
			continue;
		}
		int x=dep[a]-1,y=dep[b]-1;
		assert(x>-1);
		assert(y>-1);
		int cirnode=Tg[a];
		int cirsiz=Cir[cirnode].size();
		int posx=trg[a];
		int posy=trg[b];
		int cirposx=rk[posx];
		int cirposy=rk[posy];
		assert(cirposx<=cirsiz);
		assert(cirposy>=1&&cirposy<=cirsiz);
		int dy=0,dx=0;
		if(cirposx<cirposy){
			dx=cirposy-cirposx;
			dy=cirsiz-dx;
		}
		else{
			dy=cirposx-cirposy;
			dx=cirsiz-dy;
		}
		assert(dx>=0);
		assert(dy>=0);
		int nx=x+dx;
		int ny=y+dy;
		assert(nx>=0);
		assert(ny>=0);
		//(x,ny)(nx,y)
		int mx1=Max(x,ny);
		int mx2=Max(nx,y);
//		printf("(%d %d)\n(%d %d)\n",nx,y,x,ny);
		if(mx1!=mx2){
			if(mx1>mx2)printf("%d %d\n",nx,y);
			else printf("%d %d\n",x,ny);
			continue;
		}
		else{
			int mi1=Min(x,ny);
			int mi2=Min(nx,y);
			if(mi1!=mi2){
				if(mi1>mi2)printf("%d %d\n",nx,y);
				else printf("%d %d\n",x,ny);
				continue;
			}
			printf("%d %d\n",nx,y);
			continue;
		}
	}
	return 0;
}
posted @ 2021-08-30 13:05  Refined_heart  阅读(42)  评论(0编辑  收藏  举报