P10693 撅个题
P10693 撅个题
这个题是一个比较神奇的图论题
首先我们看到题面是这样描述的,第 $ i $ 个人想坐 $ a_i $ 个位置,于是 $ i $ 对 $ a_i $ 连边
手玩个样例会发现,我们建出来的图有以下性质:
-
前 $ n $ 个点往外连边,后 $ n $ 个点不往外连边
-
可能会存在环
-
每个点只会往外连至多一条边
有了这些性质,我们会发现,这个图我们需要考虑到以下三种情况
-
对于一条链,他一定会连到后 $ n $ 个点去,但后 $ n $ 个点可能会连不只一条链,所以考虑最长的一条,取它的贡献
-
其他的部分就是一个基环树森林,即不只一个基环树,所以我们考虑拓扑排序,把非环部分删掉
-
把剩下点的数量加到答案里,这是环,环上的点都可以满足要求
所以我们就做完了
代码如下:
# include <bits/stdc++.h>
using namespace std;
int n;
vector < int > vec[200005], vec1[200005];
bool vis[200005];
int in[200005];
int res = 0, ans = 0;
queue < int > q;
inline void dfs (int u, int d) {
if (u <= n) res = max (res, d), vis[u] = 1;
for (int i = 0; i < vec1[u].size (); ++ i) {
int v = vec1[u][i];
dfs (v, d + 1);
}
}
signed main () {
cin >> n;
for (int i = 1; i <= n; ++ i) {
int x; cin >> x;
vec[i].push_back (x);
vec1[x].push_back (i);
++ in[x];
}
for (int i = n + 1; i <= 2 * n; ++ i) {
res = 0;
dfs (i, 0);
ans += res;
}
for (int i = 1; i <= n; ++ i) if (! vis[i] && ! in[i]) q.push (i), vis[i] = 1;
for (; ! q.empty (); ) {
int u = q.front ();
q.pop ();
for (int i = 0; i < vec[u].size (); ++ i) {
int v = vec[u][i];
-- in[v];
if (! in[v]) q.push (v), vis[v] = 1;
}
}
for (int i = 1; i <= n; ++ i) if (! vis[i]) ++ ans;
cout << ans << endl;
return 0;
}