题目传送门OvO
题目描述
每年,在威斯康星州,奶牛们都会穿上衣服,收集农夫约翰在 N ( 1 < = N < = 100000 ) N(1<=N<=100000) N(1<=N<=100000)个牛棚隔间中留下的糖果,以此来庆祝美国秋天的万圣节。
由于牛棚不太大,FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案,FJ在第i号隔间上张贴了一个“下一个隔间” N e x t i ( 1 < = N e x t i < = N ) Next_i(1<=Next_i<=N) Nexti(1<=Nexti<=N),告诉奶牛要去的下一个隔间;这样,为了收集它们的糖果,奶牛就会在牛棚里来回穿梭了。
FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间,她就会停止收集糖果。
在被迫停止收集糖果之前,计算一下每头奶牛要前往的隔间数(包含起点)。
输入格式
第1行 整数n。
第2行到n+1行 每行包含一个整数 n e x t i next_i nexti 。
输出格式
n行,第i行包含一个整数,表示第i只奶牛要前往的隔间数。
第一次写蓝题题解好激动啊哈哈哈哈……
发现很多人都是用Tarjan或者记忆搜索,我就蛮奇怪
不是说不能用tarjan,而是这题完全没有必要哈,,,
get网上志同道合的一个OIer的讲解:
首先我们需要注意到一点,虽然此题也是一张有向图,但是每个点的出度有且只有 “1”。这说明什么?不需要递归,直接沿着这条唯一的路径走下去就行了…
一、为了实现这一方法,我们对每个点设置两个属性:
1、颜色 (color): 此节点第一次被访问时,这条访问他的路径是由那个节点发出的(起点)。
2、时间戳 (dfn) :此节点第一次被访问时,他到发出这条路径的起点的距离(发出节点的 dfn=0,第二个被访问的节点的 dfn = 1,第三个 dfn = 2 …)
有了这两个属性,我们就可以计算环的大小,方法如下:
1、从某一节点发出路径
2、走到某个节点上(包括起点),如果这个节点没有被染色,那么染成自己的颜色,并标记上 dfn
3、走到某个节点上,如果这个节点已经被染成了自己的颜色,那么环的大小就出来了:当前时(cnt) − 此节点 dfn
到了这一步,实际上已经解决了另一个更简单的问题:NOIP2015 信息传递。 接下来就是本题特色了
二、对于每一只奶牛(或者说每一个起点、颜色、路径),我们记录如下两个属性:
1、环的大小 (minc):每条路径最终都会进入环中,或者起点本身就在环中,我们记录下这个环的大小为之后服务
2、入环时间戳 (sucdfn) :这条路径什么时候会进入环中,同样是为之后服务的一个属性
首先讲解一下如果得到这两个属性:
1、在上一节中我们已经讲了如何初步获取环的大小,入环时间戳只要记录为那个交点的时间戳即可
2、如果走到了之前走过的节点,那么新的路径必然进入之前路径的环中,直接把这个环的大小要过来就行了。入环时间戳则分两种情况:
i. 如果这个节点不在环中,“原路径的入环时间戳 -− 原路径中此节点的时间戳 + 新路径当前时间” 即为新路径的入环时间戳;
ii. 而如果这个节点在环中,直接就是新路径当前时间。
iii. 判断方法则是 “原路径的入环时间戳 - 原路径中此节点的时间戳” 是否大于 0,综合起来就是:“ m a x ( m a x ( 原 路 径 的 入 环 时 间 戳 − 原 路 径 中 此 节 点 的 时 间 戳 ,    0 ) , 0 ) + 新 路 径 当 前 时 间 ” max(max(原路径的入环时间戳 - 原路径中此节点的时间戳, ;0),0) + 新路径当前时间” max(max(原路径的入环时间戳−原路径中此节点的时间戳,0),0)+ 新路径当前时间”
三、把上面的问题都解决了,出答案就太简单了:
1、第一节中的发现环的大小之后,答案就是“当前时间”
2、第二节中与之间走过的节点相遇并记录完信息后,答案是 “入环时间戳 + 环的大小”
至此本题已经完美解决,且没有用到任何算法。贴代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include<bits/stdc++.h> using namespace std; const int maxn = 100000 + 5; int n; int nxt[maxn]; int color[maxn]; int sucdfn[maxn]; int dfn[maxn]; int minc[maxn]; void Init() { cin >> n; for(int i = 1; i <= n; ++i) cin >> nxt[i]; memset(color, 0, sizeof(color)); memset(dfn, 0, sizeof(dfn)); memset(minc, 0, sizeof(minc)); } void Solve() { for(int cow = 1; cow <= n; ++cow) { for(int i = cow, cnt = 0; ; i = nxt[i], ++cnt) { if(!color[i]) { color[i] = cow; dfn[i] = cnt; } else if(color[i] == cow) { minc[cow] = cnt - dfn[i]; sucdfn[cow] = dfn[i]; cout << cnt << endl; break; } else { minc[cow] = minc[color[i]]; sucdfn[cow] = cnt + max(sucdfn[color[i]] - dfn[i], 0); cout << sucdfn[cow] + minc[cow] << endl; break; } } } } int main() { Init(); Solve(); return 0; }
还有你们要的最短代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include<cstdio> #include<iostream> using namespace std; const int N=1e5+5; int n,to[N],rd[N],w[N],dep[N],mk[N];char vis[N],ins[N]; int dfs(int x){ int t=to[x];vis[x]=ins[x]=1; if(!vis[t]){dep[t]=dep[x]+1;w[x]=dfs(t);w[x]+=(mk[t]?(mk[x]=(mk[x]!=2?1:0),0):1);} else if(ins[t])w[x]=dep[x]-dep[t]+1,mk[x]=1,mk[t]=(x==t?0:2); else w[x]=w[t]+1; ins[x]=0;return w[x]; } int main(){ ios::sync_with_stdio(false);cin>>n;int x; for(int i=1;i<=n;++i)cin>>to[i],++rd[to[i]]; for(int i=1;i<=n;++i)if(!rd[i])w[i]=1,dfs(i); for(int i=1;i<=n;++i)if(!vis[i])dfs(i);//totally a cycle for(int i=1;i<=n;++i)cout<<w[i]<<endl; return 0; }
最后
以上就是欢呼音响最近收集整理的关于洛谷[P2921]在农场万圣节题目描述的全部内容,更多相关洛谷[P2921]在农场万圣节题目描述内容请搜索靠谱客的其他文章。
发表评论 取消回复