题目传送门
1 /*
2 题意:第i个人选择第a[i]个人,问组成强联通分量(自己连自己也算)外还有多少零散的人
3 有向图强联通分量-Tarjan算法:在模板上加一个num数组,记录每个连通分量的点数,若超过1,则将连通点数相加
4 用总点数-ans则是零散的点
5 详细解释:http://www.bubuko.com/infodetail-927304.html
6 */
7 #include <cstdio>
8 #include <cstring>
9 #include <algorithm>
10 #include <vector>
11 #include <stack>
12 using namespace std;
13
14 const int MAXN = 1e5 + 10;
15 const int INF = 0x3f3f3f3f;
16 vector<int> G[MAXN];
17 stack<int> S;
18 int pre[MAXN], low[MAXN];
19 int instack[MAXN], num[MAXN];
20 int dfs_clock, scc_cnt;
21 int n, ans;
22
23 void DFS(int u)
24 {
25 instack[u] = 1;
26 pre[u] = low[u] = ++dfs_clock;
27 S.push (u);
28 for (int i=0; i<G[u].size (); ++i)
29 {
30 int v = G[u][i];
31 if (!pre[v])
32 {
33 DFS (v); low[u] = min (low[u], low[v]);
34 }
35 else if (instack[v] == 1)
36 {
37 low[u] = min (low[u], pre[v]);
38 }
39 }
40
41 if (low[u] == pre[u])
42 {
43 scc_cnt++;
44 for (; ; )
45 {
46 int x = S.top (); S.pop ();
47 instack[x] = 0;
48 num[scc_cnt]++;
49 if (x == u) break;
50 }
51 }
52 }
53
54 void Tarjan(void)
55 {
56 dfs_clock = scc_cnt = 0;
57 memset (pre, 0, sizeof (pre));
58 memset (low, 0, sizeof (low));
59 memset (num, 0, sizeof (num));
60 memset (instack, 0, sizeof (instack));
61 while (!S.empty ()) S.pop ();
62
63 for (int i=1; i<=n; ++i)
64 {
65 if (!pre[i]) DFS (i);
66 }
67 }
68
69 int main(void) //UVALive 6511 Term Project
70 {
71 freopen ("L.in", "r", stdin);
72
73 int t; scanf ("%d", &t);
74 while (t--)
75 {
76 ans = 0; scanf ("%d", &n);
77 for (int i=1; i<=n; ++i) G[i].clear ();
78 for (int i=1; i<=n; ++i)
79 {
80 int x; scanf ("%d", &x);
81 G[i].push_back (x);
82 if (i == x) ans++;
83 }
84
85 Tarjan ();
86 for (int i=1; i<=scc_cnt; ++i)
87 {
88 if (num[i] > 1) ans += num[i];
89 }
90 printf ("%d
", n - ans);
91 }
92
93 return 0;
94 }