CF1872F 题解
思路
我们可以先找到入度为 (即没动物害怕它)的动物,先卖掉这些动物就行了。然后进行拓扑排序,把它们害怕的动物的入度减一,直到大家形成了环后不能继续拓扑排序了就行。剩下每个连通块肯定都是一个环,因为每个点的出度都是 。我们可以利用并查集判连通块。对于每个环,我们只要让代价最小( 最小)的动物最后删掉就行了。剩下的事情就交给 SPJ 了,我们的事情已经结束了。
代码
# include <bits/stdc++.h>
using namespace std;
int t, n, a[100005], b[100005], du[100005], f[100005], x, mini[100005];
queue <int> q;
int find (int x) { //并查集找爹函数,不用讲了吧
return x == f[x] ? x : f[x] = find (f[x]); //爸爸的爸爸叫什么?爸爸的爸爸叫爸爸
}
int main () {
cin >> t;
while (t --) {
cin >> n;
for (int i = 1; i <= n; ++ i)
mini[i] = du[i] = 0, f[i] = i; //多组数据要初始化
for (int i = 1; i <= n; ++ i)
cin >> a[i], ++ du[a[i]]; //du[i] 表示有几只动物怕 i
for (int i = 1; i <= n; ++ i)
cin >> b[i];
for (int i = 1; i <= n; ++ i)
if (! du[i]) //没人怕 i
q.push (i); //先插进去
while (! q.empty ()) { //拓扑排序
x = q.front ();
q.pop ();
cout << x << ' ';
if (! -- du[a[x]])
q.push (a[x]);
}
for (int i = 1; i <= n; ++ i)
if (du[i]) //还有动物怕它,说明它在一个环内
f[find (i)] = find (a[i]); //并查集找连通块(环)
for (int i = 1; i <= n; ++ i)
if (du[i] && (! mini[find (i)] || b[mini[f[i]]] > b[i])) //找到每个连通块内代价最小的动物
mini[f[i]] = i;
for (int i = 1; i <= n; ++ i)
if (du[i] && i == mini[f[i]]) { //是这个连通块内最小的动物
for (int j = a[i]; j != i; j = a[j]) //开始遍历环
cout << j << ' ';
cout << i << ' '; //别忘了自己
}
cout << '\n'; //别忘了换行
}
return 0;
}

浙公网安备 33010602011771号