参考资料
Codeforces Round 895 (Div. 3)
F. Selling a Menagerie
基环树的定义
翻译
您是一个动物收藏家,拥有编号从1到n的n只动物的动物园。然而,维护动物园的成本非常高,所以您决定出售它!
已知每只动物只害怕另一只动物。更确切地说,动物i害怕动物 a i a_i ai( a i ≠ i a_i \neq i ai=i)。此外,每只动物的成本也是已知的,动物i的成本为 c i c_i ci。
您将按照某个固定的顺序出售所有动物。具体来说,您需要选择一些排列 p 1 , p 2 , … , p n p_1, p_2, \ldots, p_n p1,p2,…,pn,首先出售动物 p 1 p_1 p1,然后是动物 p 2 p_2 p2,以此类推,最后出售动物 p n p_n pn。
当您出售动物i时,有两种可能的结果:
- 如果在动物i之前出售了动物 a i a_i ai,则您将获得卖出动物i的 c i c_i ci金额。
- 如果在动物i之前没有卖出动物 a i a_i ai,则您将获得卖出动物i的 2 ⋅ c i 2 \cdot c_i 2⋅ci金额。(令人惊讶的是,目前害怕的动物更有价值)。
您的任务是选择出售动物的顺序,以最大化总利润。
例如,如果 a = [ 3 , 4 , 4 , 1 , 3 ] a = [3, 4, 4, 1, 3] a=[3,4,4,1,3], c = [ 3 , 4 , 5 , 6 , 7 ] c = [3, 4, 5, 6, 7] c=[3,4,5,6,7],您选择的排列是 [ 4 , 2 , 5 , 1 , 3 ] [4, 2, 5, 1, 3] [4,2,5,1,3],那么:
- 第一只被卖出的动物是动物4。动物 a 4 = 1 a_4 = 1 a4=1之前并未卖出,因此您获得 2 ⋅ c 4 = 12 2 \cdot c_4 = 12 2⋅c4=12的金额。
- 第二只被卖出的动物是动物2。动物 a 2 = 4 a_2 = 4 a2=4之前已经卖出,因此您获得 c 2 = 4 c_2 = 4 c2=4的金额。
- 第三只被卖出的动物是动物5。动物 a 5 = 3 a_5 = 3 a5=3之前并未卖出,因此您获得 2 ⋅ c 5 = 14 2 \cdot c_5 = 14 2⋅c5=14的金额。
- 第四只被卖出的动物是动物1。动物 a 1 = 3 a_1 = 3 a1=3之前并未卖出,因此您获得 2 ⋅ c 1 = 6 2 \cdot c_1 = 6 2⋅c1=6的金额。
- 第五只被卖出的动物是动物3。动物 a 3 = 4 a_3 = 4 a3=4之前已经卖出,因此您获得 c 3 = 5 c_3 = 5 c3=5的金额。
您在这个排列选择下的总利润是 12 + 4 + 14 + 6 + 5 = 41 12 + 4 + 14 + 6 + 5 = 41 12+4+14+6+5=41。请注意,41不是这个例子中的最大可能利润。
输入
输入的第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1≤t≤104),表示测试用例的数量。
然后是每个测试用例的描述。
每个测试用例描述的第一行包含一个整数 n n n ( 2 ≤ n ≤ 1 0 5 2 \le n \le 10^5 2≤n≤105),表示动物的数量。
测试用例描述的第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an ( 1 ≤ a i ≤ n 1 \le a_i \le n 1≤ai≤n, a i ≠ i a_i \neq i ai=i), a i a_i ai表示动物i害怕的动物的索引。
测试用例描述的第三行包含 n n n个整数 c 1 , c 2 , … , c n c_1, c_2, \dots, c_n c1,c2,…,cn ( 1 ≤ c i ≤ 1 0 9 1 \le c_i \le 10^9 1≤ci≤109),表示动物的成本。
保证所有测试用例中 n n n的总和不超过 1 0 5 10^5 105。
输出
输出 t t t行,每行包含对应测试用例的答案。答案应为 n n n个整数,即排列 p 1 , p 2 , … , p n p_1, p_2, \ldots, p_n p1,p2,…,pn,表示以哪个顺序出售动物以最大化利润。如果有多个可能的答案,可以输出任何一个。
思路
首先,我们认为 A → B A ~→~B A → B 表示 A A A 害怕 B B B。
易知,题目给出的是一个基环树。
在有向图中,每一个点都有唯一的一个出边的图,是基环树;
在无向图中,具有
n
n
n 个点
n
n
n 条边的连通图,是基环树。
那么我们通过观察基环树的模型可知,基环树中的环上必定有一个动物不能以二倍价格卖出(或者说必定有一个动物在卖他的时候已经不存在它害怕的动物了)。
另外我们还可以看出,基环树支链上的所有动物都能以二倍价格卖出。
根据这两个结论,我们可以试着想一下该如何安排动物的顺序。
对于支链,我们可以按照拓扑排序中的思路处理它们的顺序(由于不好解释,建议直接看代码理解)。
对于基环树中的环,我们需要让c[i]最小的动物最后被出售(即原价最低的动物最后被出售)。
只看文字很难看明白思路,直接上代码。
C
o
d
e
Code
Code
#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;
int n;
int a[N]; // a[i]:动物i害怕的动物
int c[N]; // c[i]:动物i的价格
int d[N]; // d[i]:害怕动物i的动物数量
int st[N]; // st[i]:动物i还在不在
void solve() {
cin >> n;
fill(d, d + n + 1, 0);
fill(st, st + n + 1, 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
d[a[i]] ++;
}
for (int i = 1; i <= n; i ++) {
cin >> c[i];
}
// 解决基环树中环上的支链
for (int i = 1; i <= n; i ++) {
if (d[i] == 0 && st[i]) {
int now = i;
while (d[now] == 0) {
cout << now << ' ';
st[now] = 0;
d[a[now]] --;
now = a[now];
}
}
}
/*
现在只剩下了不含支链的环
对于这个环,只有最后卖的动物不能以2 * c[i]的价格卖出
所以,为了使得卖的钱最多,需要最后卖c[i]值最小的动物
*/
for (int i = 1; i <= n; i ++) {
if (st[i]) {
int now = a[i];
int min_c = i;
while (now != i) {
if (c[now] < c[min_c]) {
min_c = now;
}
now = a[now];
}
now = a[min_c];
while (1) {
cout << now << ' ';
st[now] = 0;
if (now == min_c) {
break;
}
now = a[now];
}
}
}
cout << "\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T; cin.get();
while (T --) solve();
return 0;
}