首页 > 其他分享 >P3533 [POI2012] RAN-Rendezvous 题解

P3533 [POI2012] RAN-Rendezvous 题解

时间:2023-09-09 21:33:31浏览次数:51  
标签:dep RAN POI2012 题解 top 1000001 int fa st

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

标签:dep,RAN,POI2012,题解,top,1000001,int,fa,st
From: https://www.cnblogs.com/WrongAnswer90-home/p/17690179.html

相关文章

  • vncviewer黑屏问题解决
    如果不知道kill多少值,可通过ps-ef|grepvnc进行查看1.先kill掉现在的vnc端口进程(假设端口是1):比如:vncserver-kill:12.打开启动文件xstartup:vim~/.vnc/xstartup3.修改其中的内容如下:#!/bin/shexportXKL_XMODMAP_DISABLE=1unsetSESSION_MANAGERunsetDBUS_SESSION_BUS_AD......
  • CF1570D 题解
    思路分析前言题解区好似没有用哈希的啊。发现大家都在用map来存是否出现过数字,但是需要注意的是,map的单次查询时间复杂度是\(\mathcalO(\logn)\)的,对于大规模的数据就很可能会TLE。所以,我们可以使用哈希的方法来判断数字是否出现过。浅谈哈希哈希,是通过哈希函数将数......
  • 【题解】Educational Codeforces Round 143(CF1795)
    A.TwoTowers题目描述:有\(a,b\)两座由红蓝色方块垒成的塔,其中\(a\)的高度为\(n\);\(b\)的高度为\(m\),用R代表红色;用B代表蓝色。你可以多次把其中一座顶端的方块移到另一座的顶端(可以不移动)。问有没有一种方法可以使两座塔中均没有连续的同颜色方块。题目分析:可以......
  • UVA202 题解
    思路分析前言又是一道小模拟题,不过细节巨多,可以用来锻炼自己的代码能力。解法本题实际上就是模拟长除法的计算过程,其中每一步除法时都有被除数及其余数,当被除数出现重复时就表示出现循环节了。所以需要记录每一次的被除数及其在循环小数中的位置,需要判断当除数不够除,每一次补......
  • 题解 [CQOI2009] 中位数
    题目链接要想使得数字\(x\)是中位数,就必须选出\(k\)个小于\(x\)的数和\(k\)个大于\(x\)的数。我们考虑对数字附上特殊值,小于\(x\)的数赋值为\(-1\),大于\(x\)的数赋值为\(1\),\(x\)则赋值为\(0\),那么若一段包含\(x\)的连续序列的和等于\(0\),就可以说明这段连......
  • [题解] Codeforces Round 895 (Div. 3) F~G
    CodeforcesRound895(Div.3)F~GF.SellingaMenageri考虑如何让卖出的价格翻倍,那么自然是从\(i\toa_i\)。通过这样连边,我们可以发现,边集构成了基环树森林。显而易见的是,如果不考虑环,那么图就是拓扑图,按照拓扑关系跑一遍,就可以使得所有点价值最多。现在考虑环上的问题......
  • [AGC036C] GP 2 题解
    洛谷题目链接AT原题考虑构造出来的序列\(a\)的特征,因为每次会给\(a_i\)加\(1\),\(a_j\)加\(2\),所以每次操作后\(\suma_i\)会加上\(3\)。所以有\(\suma_i=3m\)。又因为每次操作只给一个数加\(1\),所以每次操作要么给序列加入一个奇数,要么使原来的一个奇数变成偶数......
  • [题解] CF29D Ant on the Tree
    CF29DAntontheTree题目知识点:LCA。题目传送门题意给定一棵以\(1\)为节点的树,再给定树的所有叶子节点的一个序列。现在执行一个操作:从\(1\)开始遍历每个节点,并返回根,要求每条边经过的次数一定为\(2\)。问是否能够使得访问节点序列中叶子节点的序列符合给定序列的条......
  • P2206题解
    题目大意:给定一些指令,计算需要多大的舞台。这是一道大模拟!!!只要遍历每次指令,然后判断是否摔倒,摔倒输出`-1`否则记录,最后求出面积就行了。最后附上代码1#include<bits/stdc++.h>2usingnamespacestd;3constintxx[]={-1,0,1,0},yy[]={0,1,0,-1};//不同......
  • CF1513C题解
    一道递推由于对于一个数x,可得x+10-x=10(废话)于是问题就变成了0+m次,然后x+m就变成0+x+m(还是废话)于是可以写一个递推。首先对于函数f(m)可分为m ≤9和m>9,然后可得出递推式结果为1或f(m-9)+f(m-10),所以我们可以直接求出结果再分解数位求值。最后贴上代码......