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\n", res);
    for(int i = 0; i < ans.size(); i++)
        printf("%d%c", ans[i], " \n"[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;
}
posted @ 2020-12-10 20:19  Emcikem  阅读(74)  评论(0)    收藏  举报