【图论】Selling a Menagerie—CF1872F

Selling a Menagerie—CF1872F

参考资料

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 2ci金额。(令人惊讶的是,目前害怕的动物更有价值)。

您的任务是选择出售动物的顺序,以最大化总利润。

例如,如果 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 2c4=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 2c5=14的金额。
  • 第四只被卖出的动物是动物1。动物 a 1 = 3 a_1 = 3 a1=3之前并未卖出,因此您获得 2 ⋅ c 1 = 6 2 \cdot c_1 = 6 2c1=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 1t104),表示测试用例的数量。

然后是每个测试用例的描述。

每个测试用例描述的第一行包含一个整数 n n n ( 2 ≤ n ≤ 1 0 5 2 \le n \le 10^5 2n105),表示动物的数量。

测试用例描述的第二行包含 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 1ain, 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 1ci109),表示动物的成本。

保证所有测试用例中 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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值