P3533 [POI2012] RAN-Rendezvous 题解

发布时间 2023-09-09 21:26:20作者: WrongAnswer_90

P3533 [POI2012] RAN-Rendezvous

题目大意:给定外向树森林,每次给定两个起始点,求两个点沿边移动最少步数相遇。

\(n\) 个点,\(n\) 条边,并且每个点有唯一的出边,显然构成了多棵基环树,对于每个基环树分别处理:找出环上的点,因为要求支持求出任意两点距离,前缀和一下即可。

对于询问,如果在两棵不同的基环树上(具体判断使用并查集),那么一定无解。如果在一棵基环树的同一棵子树内,求 LCA 并记录一下深度即可求出,代码实现使用的倍增。对于剩下的情况,进行分讨:

image.png

首先肯定是要花 \(dep-1\) 步走到环上,接下来有两种选择:从 \(top_x\) 走到 \(top_y\) 或者从 \(top_y\) 走到 \(top_x\)。具体的,我们给 \(1,2,3,5\) 赋权值为 \(1,2,3,4\) 红色的距离就是 \(4-1=3\),蓝色的距离是 \(1-4+4\)\(4\) 是因为正好越过了整个环。

感觉思维难度完全到不了紫,实现稍微麻烦一点,遵循题目中的条件,判断自环等特殊情况,一定要注意判断环的方向!

	int n,q,cnt=1,flag,tot,f[1000001],loop[1000001],val[1000001],maxn[1000001],vis[1000001],a[1000001],dis[1000001],head[1000001],to[1000001],nex[1000001],dep[1000001],fa[1000001][31],top[1000001];
	inline void add(int x,int y){to[++cnt]=y,nex[cnt]=head[x],head[x]=cnt;}
	stack<int> st;
	void dfs(int k,int from)
	{
		vis[k]=1,!flag?st.e(k),1:1;
		for(int i=head[k];i;i=nex[i])
		{
			if(i==(from^1))continue;
			if(!flag&&vis[to[i]])
			{
				while(st.top()!=to[i])loop[++tot]=st.top(),st.pop();
				loop[++tot]=st.top(),st.pop(),flag=1;
				while(!st.empty())st.pop();
			}
			if(!vis[to[i]])dfs(to[i],i);
		}
		!flag?st.pop(),1:1;
	}
	void dfs2(int k,int father)
	{
		top[k]=top[father],dep[k]=dep[father]+1,fa[k][0]=father;
		for(int i=1;i<=30;++i)fa[k][i]=fa[fa[k][i-1]][i-1];
		for(int i=head[k];i;i=nex[i])if(vis[to[i]]==1&&to[i]!=father)dfs2(to[i],k);
	}
	inline int LCA(int x,int y)
	{
		if(dep[x]>dep[y])swap(x,y);
		for(int i=30;i>=0;--i)if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
		for(int i=30;i>=0;--i)if(fa[y][i]!=fa[x][i])y=fa[y][i],x=fa[x][i];
		return x==y?x:fa[x][0];
	}
	int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
	inline void mian()
	{
		read(n,q);int x,y,z,lca,t,w;
		for(int i=1;i<=n;++i)f[i]=i;
		for(int i=1;i<=n;++i)read(a[i]),add(i,a[i]),add(a[i],i),f[find(a[i])]=find(i);
		for(int i=1;i<=n;++i)
		{
			if(vis[i])continue;
			flag=tot=0,dfs(i,0);
			if(a[loop[1]]!=loop[2])reverse(loop+1,loop+1+tot);
			for(int j=1;j<=tot;++j)vis[loop[j]]=2;
			for(int j=1;j<=tot;++j)maxn[loop[j]]=tot,val[loop[j]]=val[loop[j-1]]+1,top[0]=loop[j],dfs2(loop[j],0);
		}
		while(q--)
		{
			read(x,y);
			if(find(x)!=find(y)){puts("-1 -1");continue;}
			if(top[x]==top[y])lca=LCA(x,y),write(dep[x]-dep[lca]),write(dep[y]-dep[lca],'\n');
			else
			{
				z=val[top[y]]-val[top[x]];if(z<0)z+=maxn[top[x]];
				t=val[top[x]]-val[top[y]];if(t<0)t+=maxn[top[x]];
				if(max(dep[x]-1+z,dep[y]-1)< max(dep[y]-1+t,dep[x]-1)
				|| max(dep[x]-1+z,dep[y]-1)==max(dep[y]-1+t,dep[x]-1)&&min(dep[x]-1+z,dep[y]-1)< min(dep[y]-1+t,dep[x]-1)
				|| max(dep[x]-1+z,dep[y]-1)==max(dep[y]-1+t,dep[x]-1)&&min(dep[x]-1+z,dep[y]-1)==min(dep[y]-1+t,dep[x]-1)&&dep[x]-1+z>=dep[y]-1)write(dep[x]-1+z),write(dep[y]-1,'\n');
				else write(dep[x]-1),write(dep[y]-1+t,'\n');
			}
		}
	}