Codeforces Round #660 (Div. 2) D. Captain Flint and Treasure

链接

https://codeforces.com/contest/1388/problem/D

题意

数组 $a,b$ 长度为 $n$,选择一个 $i$,进行如下操作

  • $ans+=a_i$
  • $a_{b_i}+=a_i(b_i\ne-1)$

求一个 $1\sim n$ 的排列使按此排列操作得到的 $ans$ 最大

思路

$i$ 对 $b_i$ 有贡献,即 $i$ 可以到达 $b_i$,反向建图,我们可以得到若干个树形图,树根为 $b_i=-1$ 的点

对于某个节点 $u$ 的儿子 $v$,若 $a_v>0$,贡献为正,则先对 $v$ 进行操作,否则 $v$ 只能在 $u$ 操作后再操作,可以把 $v$ 存进数组 $t$

遍历完所有点后,倒序操作数组 $t$ 中的所有点,因为后加入的点可能为先加入点的祖先

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,b[N],res[N],cnt;
long long a[N],tot;
vector<int> G[N],t;
void dfs(int u,int fa) {
for(auto v:G[u]) dfs(v,u);
if(a[u]>0) res[++cnt]=u,tot+=a[u],a[fa]+=a[u];
else t.push_back(u);
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) {
if(b[i]==-1) continue;
G[b[i]].push_back(i);
}
for(int i=1;i<=n;i++) if(b[i]==-1) dfs(i,0);
for(int i=t.size()-1;i>=0;i--) res[++cnt]=t[i],tot+=a[t[i]];
printf("%lld\n",tot);
for(int i=1;i<=n;i++) printf("%d ",res[i]);
puts("");
return 0;
}