Selling a Menagerie 的题解
题面大意
动物园里有 \(n\) 个动物,第 \(i\) 个动物害怕第 \(a_i\) 个动物,第 \(i\) 个动物价值 \(c_i\) 元。现在我要将这些动物全部卖掉。显然,卖掉的动物编号可以构成一个排列 \(p\)。
考虑卖掉这些动物时:
- 若 \(a_i\) 在 \(i\) 还没有卖掉之前就被卖掉了,现在卖掉 \(i\),可以获得 \(c_i\) 元;
- 若 \(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;
}