[NOIP2015] 信息传递

嘟嘟嘟

题面很清楚,就是让求最小环。

有两种做法:

F1:用带权并查集。将每一条边连接的两个点所在集合合并,如果已经在一个集合,说明形成了环,用dis[x] + dis[y] + 1更新ans。因为图中的边是有向的,所以并查集也必须又向,对于边(x->y),可以规定x所在集合向y和并,那么同时dis[x] = dis[y] + 1。

F2:拓扑排序,先删去所有入度为0的点,因为这些点肯定不在环中。然后把这些点连接着的点的入度-1,继续删去所有入度为0的点。最后的图一定是一个只有简单环且不连通的图,因为题中说每一个点出度为1。对于每一个环,dfs求大小即可。

这里给出F1的代码。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 2e5 + 5;
21 inline ll read()
22 {
23   ll ans = 0;
24   char ch = getchar(), last = ' ';
25   while(!isdigit(ch)) last = ch, ch = getchar();
26   while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
27   if(last == '-') ans = -ans;
28   return ans;
29 }
30 inline void write(ll x)
31 {
32   if(x < 0) x = -x, putchar('-');
33   if(x >= 10) write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 
37 int n, ans = INF;
38 
39 int p[maxn], dis[maxn];
40 void init()
41 {
42   for(int i = 1; i <= n; ++i) p[i] = i;
43 }
44 int Find(int x)
45 {
46   if(x != p[x])
47     {
48       int las = p[x];
49       p[x] = Find(p[x]);
50       dis[x] += dis[las];
51     }
52   return p[x];
53 }
54 void merge(int x, int y)
55 {
56   int px = Find(x), py = Find(y);
57   if(px != py) p[px] = py, dis[x] = dis[y] + 1;
58   else ans = min(ans, dis[x] + dis[y] + 1);
59 }
60 
61 int main()
62 {
63   n = read();
64   init();
65   for(int i = 1; i <= n; ++i)
66     {
67       int x = read();
68       merge(i, x);
69     }
70   write(ans), enter;
71   return 0;
72 }
View Code
原文地址:https://www.cnblogs.com/mrclr/p/9876047.html