题面大意

动物园里有 nn 个动物,第 ii 个动物害怕第 aia_i 个动物,第 ii 个动物价值 cic_i 元。现在我要将这些动物全部卖掉。显然,卖掉的动物编号可以构成一个排列 pp

考虑卖掉这些动物时:

  1. aia_iii 还没有卖掉之前就被卖掉了,现在卖掉 ii,可以获得 cic_i 元;
  2. aia_iii 还没有卖掉之前没被卖掉,现在卖掉 ii,可以获得 2ci2·c_i 元;

构造并输出赚钱最多的动物卖出顺序。

思路

显而易见,题目可以转化为图论进行解决。

将第 ii 个点向 aia_i 连一条无向边,构成一个 nn 个点 nn 条边的基环树森林。

如果并没有其他的动物害怕第 ii 只动物,那么将第 ii 只动物卖出后并不会因为缺少了自己害怕的动物而减少价值。反映在图中即是将没有入度的点依次卖出,就是跑一次拓扑序。

因为这个图是一个基环树森林,所以在将所有的入度为 00 的点全部删除之后剩下的节点必然满足对于任意的 xxyy 有且仅有一条边将他们链接。

我们可以贪心的考虑删除掉环中的那一个点。

假设节点 ii 害怕的动物为 aia_i,其价格为 cic_i,这个环内的节点为 ss,那么肯定有一个动物会因为自己害怕的动物已经卖出而变得便宜,不放假设变便宜的动物为 xx

这个环的价值就为 is(2×ci)cx\sum_{i\in s}( 2\times c_i)-c_x。因为 is(2×ci)\sum_{i\in s}( 2\times c_i) 是一个定值,所以要使价值最大化就应该使 cxc_x 尽可能的小。如果提前卖掉的动物为为 mm 那么 x=amx=a_m,所以提前删除的点 mm 应该使 sams_{a_m} 最小。

在每一次将环内的一个点卖出后,剩下的节点就可以通过拓扑序的方式依次删除。

注意,因为这是一个基环树森林,所以可能存在多个环。

AC Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,du[N],s[N];
vector<int> v[N];
queue<int> qq;
bool vis[N];
struct node{
	int x,v;
	friend bool operator < (const node a,const node b){
		return a.v>b.v;
	}
};
priority_queue<node>q;
void solve(){
	cin>>n;
	int cnt=0;
	for(int i=1;i<=n;i++){
		v[i].clear();
		du[i]=0;
		vis[i]=0;
		s[i]=0;
	}
	while(!q.empty()){
		q.pop();
	}
	for(int i=1,x;i<=n;i++){
		cin>>x;
		du[x]++;
		v[i].push_back(x);
	}
	for(int i=1;i<=n;i++){
		cin>>s[i];
	}
	for(int i=1;i<=n;i++){
		if(du[i]==0){
			qq.push(i);
			cnt++;
			vis[i]=1;
			cout<<i<<" ";
		}
	}
	while(!qq.empty()){
		int x=qq.front();
		qq.pop();
		for(int i:v[x]){
			du[i]--;
			if(du[i]==0){
				cout<<i<<" ";
				qq.push(i);
				vis[i]=1;
				cnt++;
			}
		}
	}
	if(cnt==n){
		return;
	}
	for(int i=1;i<=n;i++){
		if(vis[i]){
			continue;
		}
		int p=0;
		while(vis[v[i][p]]) p++;
		
		q.push({v[i][p],s[i]});
	}
	while(cnt<n){
		while(vis[q.top().x]) q.pop();
		qq.push(q.top().x);
		cnt++;
		vis[q.top().x]=1;
		cout<<q.top().x<<" ";
		while(!qq.empty()){
			int x=qq.front();
			qq.pop();
			for(int i:v[x]){
				du[i]--;
				if(du[i]==0&&!vis[i]){
					cout<<i<<" ";
					qq.push(i);
					cnt++;
					vis[i]=1;
				}
			}
		}
	}cout<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}