「CF1872F」 Selling a Menagerie
题意
有 \(n\) 种动物,第 \(i\) 只动物有且只有一个害怕的动物 \(a_i\) 和价值 \(c_i\)。现在把所有的动物都卖出去,若 \(a_i\) 在 \(i\) 前面卖出去,将获得 \(c_i\) 的收益;否则将多获得 \(c_i\) 的收益。构造一种使收益最大的卖出顺序。
思路
这题很明显是图,显然对于每一个动物 \(i\),先卖 \(a_i\) 比先卖 \(i\) 更优。
可以从 \(i\) 向 \(a_i\) 连边,然后跑一遍拓扑,一边遍历一边输出当前节点。
但是,因为每个点都有一条出边,图中肯定会有一个环,拓扑后还剩下环内的节点没有考虑。
对于环内的点,先删去任何一个点都会影响后续的点,所以找到价值最小的点,从它开始删。
拓扑 \(O(n)\),遍历环 \(O(n)\),总时间复杂度 \(O(n)\)。
Code(无坑)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define read(x) scanf("%lld",&x)
const ll maxn=100005;
ll t,n,a[maxn],c[maxn],du[maxn],pre[maxn],mn[maxn];
vector<ll>h;
inline ll find(ll x){return x==pre[x]?x:pre[x]=find(pre[x]);}
inline void solve(){
read(n);
for(ll i=1;i<=n;++i)pre[i]=i,du[i]=mn[i]=0;
for(ll i=1;i<=n;++i)read(a[i]),++du[a[i]];
for(ll i=1;i<=n;++i)read(c[i]);
queue<ll>p;
for(ll i=1;i<=n;++i)if(!du[i])p.push(i);
while(!p.empty()){
ll u=p.front();p.pop();
printf("%lld ",u);
if(!--du[a[u]])p.push(a[u]);
}
for(ll i=1;i<=n;++i){
if(du[i]){
pre[find(i)]=find(a[i]);
}
}
for(ll i=1;i<=n;++i){
if(du[i]&&(mn[find(i)]==0||c[mn[pre[i]]]>c[i])){
mn[pre[i]]=i;
}
}
for(ll i=1;i<=n;++i){
if(du[i]&&mn[pre[i]]==i){
for(ll j=a[i];j!=i;j=a[j]){
printf("%lld ",j);
}
printf("%lld ",i);
}
}
puts("");
}
signed main(){
read(t);
while(t--){
solve();
}
return 0;
}

浙公网安备 33010602011771号