Selling a Menagerie 的题解

题面大意

动物园里有 \(n\) 个动物,第 \(i\) 个动物害怕第 \(a_i\) 个动物,第 \(i\) 个动物价值 \(c_i\) 元。现在我要将这些动物全部卖掉。显然,卖掉的动物编号可以构成一个排列 \(p\)

考虑卖掉这些动物时:

  1. \(a_i\)\(i\) 还没有卖掉之前就被卖掉了,现在卖掉 \(i\),可以获得 \(c_i\) 元;
  2. \(a_i\)\(i\) 还没有卖掉之前没被卖掉,现在卖掉 \(i\),可以获得 \(2·c_i\) 元;

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

思路

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

将第 \(i\) 个点向 \(a_i\) 连一条无向边,构成一个 \(n\) 个点 \(n\) 条边的基环树森林。

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

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

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

假设节点 \(i\) 害怕的动物为 \(a_i\),其价格为 \(c_i\),这个环内的节点为 \(s\),那么肯定有一个动物会因为自己害怕的动物已经卖出而变得便宜,不放假设变便宜的动物为 \(x\)

这个环的价值就为 \(\sum_{i\in s}( 2\times c_i)-c_x\)。因为 \(\sum_{i\in s}( 2\times c_i)\) 是一个定值,所以要使价值最大化就应该使 \(c_x\) 尽可能的小。如果提前卖掉的动物为为 \(m\) 那么 \(x=a_m\),所以提前删除的点 \(m\) 应该使 \(s_{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;
}
posted @ 2024-07-14 13:41  未抑郁的刘大狗  阅读(1)  评论(0编辑  收藏  举报