这里是蒟蒻我的第一篇博客qwq。用来记录大蒟子学习OI的心路历程以及学习经验的个人总结。如果有错误,请各位看官老爷及时指出,咱一定会改,毕竟要一直学习嘛。各种思路及写法仅供参考,你拿去A题也无所谓嘛。。
正文
题目大意
一张有向图,共n个点,每一个点i都有且只有一条出边Next[i](可以指向自己,即有自环)。现有n头牛分别从n个点出发,走到每个点所指向的那个点,一直走下去,直到再次走到已走过的点为止,求每头牛走过的房间数(最后一个重复的点不算)。
分析
显然题目是与环有关,可以用强连通。。。但是有dalao说太大材小用了,因为每个点只有一个出边,所以根本不需要递归,用两个for就够了。
安利大佬博文,尊重版权,先%为敬。
https://www.luogu.org/blog/planetarian/solution-p2921
所以这里我们只需要
一、先处理每一个点。
来波定义。
const int AC=100005;
int to[AC],color[AC],dfn[AC];
to显然是存的每一个点所能到达的另一个点。
从起始点cow开始遍历,枚举。
cnt记录当前步数(访问点的顺序)。
记录点i的起始点color[i],以及从起始点cow访问到它的次序dfn[i]。(起始点的dfn=0,访问到的第一个点dfn=1……)
for ( int cow=1;cow<=n;cow++ ){
for ( int i=cow,cnt=0;;i=to[i],cnt++){
……
……
}
}
搜到一个点时,若没有被访问过,则将点i的起始点color[i]设为cow,并把其在以cow为起始点的路径中的访问顺序dfn[i]赋值为当前的步数。
if ( !color[i] ){
color[i]=cow;
dfn[i]=cnt;
}
二、处理路径(环)
再来一波定义
int mir[AC],suc[AC];
mir[i]是点i所在环的大小,suc[i]是点i进入该环的时间戳。
这里有人就会问,时间戳是什么???
时间戳在这里,是指以某一点为起点时,第一次进入该环的步数。其实刚才的dfn也是时间戳……
求环的大小mir
对于在以某个起始点开始遍历的路径来说,如果当前访问的点为i,color[i]已经被标记为当前所遍历路径的起始点,那么显然,环的大小就是当前的步数(cnt)-当前遍历点的初次遍历时间戳(dfn[i])。而此时的入环时间戳显然就是第一次遍历该点时的时间戳(dfn[i])。
注意:环的大小和入环时间戳都是以起始点为依据存储的!
if ( color[i]==cow ){
mir[cow]=cnt-dfn[i];
suc[cow]=dfn[i];
}
调用已有答案
划重点!!!
总感觉那位大佬这块讲的不是很明白(认真划掉)
我们先假设当前已求出了对某一个点向后遍历的路径的各种东西。
现在我们要遍历一个新的路径,我们设这个序列里的某点为x。
如果某一个x遍历到了已经求过的路径中的某个点y,那么这里y存在两种情况。
点y在环中
显然这样新路径上的入环时间戳就是当前的步数。
suc[新路径]=cnt;
点y不在环中
如果是这样,那y点距离其路径上的环一定有一段距离,那么这段距离显然是原路径的入环时间戳-当前遍历到的点的时间戳(请认真多想几遍)。
那么,新路径的入环时间戳就是这段距离+新路径当前的步数。
suc[新路径]=(suc[原路径]-dfn[y])+cnt;
整理一下,显然能得到如下公式。
suc[新路径]=cnt+max(suc[原路径]-dfn[y],0);
三、处理答案
在第一部分处理每一个点的过程中,找到环的大小之后,答案就是“当前时间”。(因为找到环就是走到了已访问过的点)
第二部分处理路径中,与之前访问过的节点相遇并更新当前新路径信息后,答案是 “入环时间戳 + 环的大小”
完结撒花!!!
下面激动人心的上代码:
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
#include<math.h>
#include<vector>
using namespace std;
const int AC=100005;
int n,to[AC];
int color[AC],dfn[AC];
int mir[AC],suc[AC];
int main(){
scanf("%d",&n);
memset(color,0,sizeof(color));
memset(dfn,0,sizeof(dfn));
memset(mir,0,sizeof(mir));
for ( int i=1;i<=n;i++ ){
scanf("%d",&to[i]);
}
for ( int cow=1;cow<=n;cow++ ){
for ( int i=cow,cnt=0;;i=to[i],cnt++){
if ( !color[i] ){
color[i]=cow;
dfn[i]=cnt;
}
else if ( color[i]==cow ){
mir[cow]=cnt-dfn[i];
suc[cow]=dfn[i];
printf("%d\n",cnt);
break;
}
else{
mir[cow]=mir[color[i]];
suc[cow]=cnt+max(suc[color[i]]-dfn[i],0);
printf("%d\n",suc[cow]+mir[cow]);
break;
}
}
}
return 0;
}