NOIP2015 信息传递 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。

题目链接:https://www.vijos.org/p/1979

描述

有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 TiT_iTi 的同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?

格式

输入格式

输入共 2 行。

第 1 行包含 1 个正整数 n,表示 n 个人。

第 2 行包含 n 个用空格隔开的正整数 T1T_1T1, T2T_2T2, … … , TnT_nTn,其中第 i 个整数TiT_iTi表示编号为 i 的同学的信息传递对象是编号为 TiT_iTi 的同学, TiT_iTi ≤ n 且 TiT_iTi ≠ i。

数据保证游戏一定会结束。

输出格式

输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。

样例1

样例输入1

5
2 4 2 3 1

样例输出1

3

限制

对于 30%的数据, n ≤ 200;

对于 60%的数据,n ≤ 2500;

对于 100%的数据,n ≤ 200000。

提示

【输入输出样例 1 说明】

图片

游戏的流程如图所示。当进行完第 3 轮游戏后,4 号玩家会听到 2 号玩家告诉他自己的生日,所以答案为 3。当然,第 3 轮游戏后,2 号玩家、3 号玩家都能从自己的消息来源得知自己的生日,同样符合游戏结束的条件。

来源

NOIP 2015 提高组 Day 1 第二题

 

分析:

把i和Ti连边,一个人从别人口中得知自己生日的前提是图中有环,因此问题转化为寻找最小环。

两个比较容易想到的思路:

1.bfs,如果一个点被访问了两次就说明有环,用第二次访问到该点的步数-第一次访问到该点的步数更新答案。

但是bfs在最坏情况下复杂度会升到O(n*n),只能通过60%的数据。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<queue>
 6 
 7 const int MAXN = 200002;
 8 inline void read(int &x)
 9 {
10     char ch = getchar(),c = ch;x = 0;
11     while(ch < '0' || ch > '9') c = ch,ch = getchar();
12     while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar();
13     if(c == '-') x = -x;
14 }
15 
16 int n,t,cnt,ans;
17 int head[MAXN],vis[MAXN],step[MAXN];
18 
19 struct Edge
20 {
21     int f,t,nxt;
22 }e[MAXN];
23 
24 void insert(int f,int t)
25 {
26     e[++cnt].f = f,e[cnt].t = t;
27     e[cnt].nxt = head[f];
28     head[f] = cnt;
29 }
30 
31 inline int Min(int a,int b)
32 {return a<b?a:b;}
33 
34 std::queue <int> q;
35 
36 void bfs(int u)
37 {
38     memset(vis,0,sizeof(vis));
39     memset(step,0,sizeof(step));
40     q.push(u);
41     vis[u] = 1;
42     int tmp,v;
43     while(!q.empty())
44     {
45         tmp = q.front();
46         q.pop();
47         for(int i = head[tmp];i;i = e[i].nxt)
48         {
49             v = e[i].t;
50             if(vis[v]){
51                 ans = Min(ans,step[tmp]+1-step[v]);
52                 return;
53             }
54             vis[v] = 1;
55             step[v] = step[tmp]+1;
56             q.push(v);
57         }    
58     }
59 }
60 
61 int main()
62 {
63     read(n);
64     for(int i = 1;i <= n;++ i)
65     {
66         read(t);
67         insert(i,t);
68     }
69     ans = 2147483645;
70     for(int i = 1;i <= n;++ i)
71         if(!vis[i])
72             bfs(i);
73     printf("%d\n",ans);
74     return 0;
75 }
BFS 60'

2.tarjan缩点,答案即为元素数量最少但大于1的强连通分量的大小。

此为正解。

AC代码:

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<stack>
 6 
 7 const int MAXN = 200002;
 8 inline void read(int &x)
 9 {
10     char ch = getchar(),c = ch;x = 0;
11     while(ch < '0' || ch > '9') c = ch,ch = getchar();
12     while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar();
13     if(c == '-') x = -x;
14 }
15 
16 int n,t,cnt,dfn_cnt,part,ans;
17 int head[MAXN],belong[MAXN],sum[MAXN],dfn[MAXN],low[MAXN];
18 
19 struct Edge
20 {
21     int f,t,nxt;
22 }e[MAXN];
23 
24 void insert(int f,int t)
25 {
26     e[++cnt].f = f,e[cnt].t = t;
27     e[cnt].nxt = head[f];
28     head[f] = cnt;
29 }
30 
31 inline int Min(int a,int b)
32 {return a<b?a:b;}
33 
34 std::stack <int> s;
35 
36 void Tarjan(int u)
37 {
38     dfn[u] = low[u] = ++ dfn_cnt;
39     s.push(u);
40     for(int i = head[u];i;i = e[i].nxt)
41     {
42         int v = e[i].t;
43         if(!dfn[v])
44         {
45             Tarjan(v);
46             low[u] = Min(low[u],low[v]);
47         }
48         else if(!belong[v])
49             low[u] = Min(low[u],dfn[v]);
50     }
51     if(dfn[u] == low[u])
52     {
53         part ++;
54         while(!s.empty())
55         {
56             int tmp = s.top();
57             s.pop();
58             belong[tmp] = part;
59             sum[part] ++;
60             if(tmp == u) break;
61         }
62     }
63 }
64 
65 int main()
66 {
67     read(n);
68     for(int i = 1;i <= n;++ i)
69     {
70         read(t);
71         insert(i,t);
72     }
73     for(int i = 1;i <= n;++ i)
74         if(!dfn[i])
75             Tarjan(i);
76     ans = 2147483645;
77     for(int i = 1;i <= part;++ i)
78         if(sum[i] > 1 && sum[i] < ans)
79             ans = sum[i];
80     printf("%d\n",ans);
81     return 0;
82 }

 

posted @ 2017-10-26 16:32 超时空灰狼菌 阅读(...) 评论(...) 编辑 收藏