传送门
给出两个数组(a,b),需要对每一个(i)进行操作,(ans += a[i]),如果说(b_i ≠ -1),那么就把(a[b[i]] += a[i])
求出(ans)和操作的下标情况
首先分析一下如果对于所有的(b[i]≠-1)的情况,如果(a[b[i]])加上了(a[i]),那么(a[b[b[i]]])可能也要加上(a[i]),也就是说存在累加性。那么就是说对于所有的(a[i]≥0 and b[i]≠-1)的情况,我们就考虑最优累加,(b[i])小的放前面,(b[i])大的放后面,就能实现累加了。
而对于(a[i] < 0 and b[i] ≠ -1)的情况,相当于按照(b[i])去从大到小存,防止累加的情况
但是不能简单的用vector去存放,因为(a[i])的值其实是动态更新的,所有建立一个拓扑排序,如果说存在(bb[i] ≠ -1)的情况,那么久在(i)和(b[i])连一条有向边,然后跑一下拓扑排序。
对于有优先级顺序,且对后面值具有累加性的情况下,可以考虑下拓扑排序
#include <iostream>
#include <cstdio>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
ll a[N];
int b[N], in[N], n;
struct Edge{
int to, next;
}e[N << 1];
int head[N], tot;
void add(int u, int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
}
void topu(){
queue <int> q;//小顶堆
for(int i = 1; i <= n; i++){
if(!in[i]) q.push(i);
}
std::vector<int> ans;
ll res = 0;
stack<int> ss;
while(!q.empty()){
int u = q.front();
q.pop();
if(a[u] >= 0) ans.push_back(u), res += a[u];
else ss.push(u);
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(a[u] >= 0) a[v] += a[u];
in[v]--;
if(!in[v]) q.push(v);
}
}
while(!ss.empty()){
int now = ss.top();
ss.pop();
res += a[now];
ans.push_back(now);
}
printf("%lld
", res);
for(int i = 0; i < ans.size(); i++)
printf("%d%c", ans[i], "
"[i == ans.size() - 1]);
}
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) {
add(i, b[i]);
in[b[i]]++;
}
}
topu();
return 0;
}