今天我来给大家带来一片蒟蒻题解 ~~真香
LGOJ P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm
题目描述
每年&#xff0c;在威斯康星州&#xff0c;奶牛们都会穿上衣服&#xff0c;收集农夫约翰在N(1<&#61;N<&#61;100&#xff0c;000)个牛棚隔间中留下的糖果&#xff0c;以此来庆祝美国秋天的万圣节。
由于牛棚不太大&#xff0c;FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案&#xff0c;FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<&#61;Next_i<&#61;N)&#xff0c;告诉奶牛要去的下一个隔间&#xff1b;这样&#xff0c;为了收集它们的糖果&#xff0c;奶牛就会在牛棚里来回穿梭了。
FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间&#xff0c;她就会停止收集糖果。
在被迫停止收集糖果之前&#xff0c;计算一下每头奶牛要前往的隔间数&#xff08;包含起点&#xff09;。
输入格式
第1行 整数n。
第2行到n&#43;1行 每行包含一个整数 next_i 。
输入输出样例
in 1:
4
1
3
2
3
out 1:
1
2
2
3
思路引导&#xff1a;
clearly&#xff0c;我们可以发现cow是一圈圈走的。看到圈我们想到什么&#xff1f;对&#xff0c;是环。怎样快速地判断一根环呢&#xff1f;不会是O(n)查找吧。。。这时&#xff0c;我们就可以使用一种特别简便的数据结构&#xff0c;就是并查集。并查集的算法研究&#xff0c;可以追溯到20世纪30年代&#xff0c;上海黑帮黑吃黑。不同于黑帮互殴的是&#xff0c;并查集的帮派只看coder的意思&#xff0c;没有流血伤亡&#xff0c;全是光荣革命&#xff0c;通过coder的淳淳教导&#xff08;启发式搜索&#xff09;&#xff0c;还可以加快黑帮合并的速度&#xff0c;还有一种更加strong的办法&#xff0c;叫做路径压缩&#xff0c;可以是黑帮的每个人都直接听命于老大&#xff0c;时间复杂度直接降到了常数级别&#xff01;这么好的方法&#xff0c;我们想到了&#xff0c;当然要用一用啦。在这道题中&#xff0c;我们的用法又略有不同。话不多说&#xff0c;上代码。代码有详解。
/*Name: Trick or Treat on the Farm Author: JackDate: 05-04-19 20:38Description: printf("%d\n",inloop[now]&#43;loopsize[now]);
节点&#xff1a;房间。
环&#xff1a;cow按指示走直到不得不停时走过节点组成的环
*/
#include
using namespace std;
const int maxn &#61; 1e6&#43;5;
int dfn[maxn],inloop[maxn],col[maxn],nxt[maxn],loopsize[maxn];
//dfn:时间戳&#xff0c;记录访问到当前节点时的时间&#xff0c;辅助inloop
//inloop:入环时间戳&#xff0c;记录访问到这个环时&#xff0c;时间是多少
//col:并查集&#xff0c;记录每个节点的祖先。本题较特殊&#xff0c;不是用具体的节点作为祖先&#xff0c;而是用一个概念“牛”&#xff0c;将牛的编号作为祖先&#xff0c;所以并查集一般的操作都用不了。As the matter of fact&#xff0c;本题是可以用节点作为祖先解决的。但那样思路会有点卡壳&#xff0c;所以还是hack掉了。
//nxt&#xff1a;每个节点连接的下一个节点
//loopsize&#xff1a;环的节点个数
int n;void init(){scanf("%d",&n);for(int i&#61;1;i<&#61;n;i&#43;&#43;)scanf("%d",&nxt[i]);memset(dfn,0,sizeof(dfn));memset(col,0,sizeof(col));memset(loopsize,0,sizeof(loopsize));//输入以及都清空。
}
void work(){for(int now&#61;1;now<&#61;n;now&#43;&#43;){//按每只牛循环。for(int i&#61;now,cnt&#61;0;;i&#61;nxt[i],cnt&#43;&#43;){
//按访问顺序访问if(!col[i]){col[i]&#61;now;//这个房间归这头牛了dfn[i]&#61;cnt;//时间戳就是cnt}else if(col[i]&#61;&#61;now){//如果回到了自己的地盘
loopsize[now]&#61;cnt-dfn[i];
//环的大小&#61;访问最后一个点的时间-访问第一个点的时间inloop[now]&#61;dfn[i];
//入环时间&#61;访问第一个点的时间printf("%d\n",cnt);break;} else {
//如果来到了他人的领地&#xff0c;就扩充自己的领地loopsize[now]&#61;loopsize[col[i]];//环的大小&#61;他人领地的大小
inloop[now]&#61;cnt&#43;max(inloop[col[i]]-dfn[i],0);
//入环时间&#61;访问最后一个点的时间加后面这一串&#xff0c;留作思考题&#xff0c;自己想
break;
}
}
}
}
int main(){
init();
work();
return 0;
}
完美结束
OK&#xff0c;谢谢观赏&#xff0c;各位看官觉得写得好的点个赞呗~~