cf1388D Captain Flint and Treasure

传送门
给出两个数组(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;
}
原文地址:https://www.cnblogs.com/Emcikem/p/14117156.html