제출 #1309826

#제출 시각아이디문제언어결과실행 시간메모리
1309826harryleeeJobs (BOI24_jobs)C++20
40 / 100
260 ms86064 KiB
#include<bits/stdc++.h>
using namespace std;

const int maxn = 3e5 + 5;
int n;
long long init_money, res;
pair<long long, int> a[maxn];
vector<int> adj[maxn];

// Thay vector bằng multiset
multiset<pair<long long, long long>> ms[maxn];

inline void DFS(int u) {
    for (int v : adj[u]) {
        DFS(v);
        
        // Kỹ thuật Small-to-Large Merging:
        // Nếu tập con lớn hơn tập cha, hoán đổi để luôn insert từ tập nhỏ vào tập lớn
        if (ms[v].size() > ms[u].size()) {
            swap(ms[u], ms[v]); 
        }
        
        // Gộp các phần tử từ con vào cha
        for (auto &x : ms[v]) {
            ms[u].insert(x);
        }
        ms[v].clear(); // Xóa bộ nhớ tập con sau khi gộp
    }

    // Nếu là node ảo (gốc 0), ta không xử lý logic gộp lỗ lãi của chính nó
    if (u == 0) return;

    // Tạo job hiện tại: {cost (số âm), profit}
    pair<long long, long long> cur = {min(0LL, a[u].first), a[u].first};

    // Logic Greedy:
    // Nếu job hiện tại đang lỗ (cur.second <= 0), ta cần "kéo" các job có lãi từ con lên để bù.
    // Ta chọn job có cost lớn nhất (ít âm nhất - dễ thực hiện nhất) từ multiset.
    // Trong multiset (sort tăng dần theo first), phần tử lớn nhất nằm ở cuối (rbegin).
    while (cur.second <= 0 && !ms[u].empty()) {
        // Lấy phần tử tốt nhất (cuối set)
        auto it = prev(ms[u].end()); 
        pair<long long, long long> best = *it;
        ms[u].erase(it); // Xóa khỏi set

        // Gộp vào job hiện tại
        // Cost mới = min(cost cũ, tiền lời hiện có + cost của job con)
        cur.first = min(cur.first, cur.second + best.first);
        cur.second += best.second;
    }

    // Nếu sau khi gộp mà có lãi, đẩy lại vào set để cha của u sử dụng
    if (cur.second > 0) {
        ms[u].insert(cur);
    }
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);

    cin >> n >> init_money;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i].first >> a[i].second;
        adj[a[i].second].push_back(i);
    }

    DFS(0);

    // Xử lý tại gốc 0: Chọn các việc làm được
    // Duyệt từ việc dễ nhất (cost lớn nhất -> cuối set) về việc khó nhất
    // Lưu ý: multiset không hỗ trợ pop_back như vector, ta dùng reverse iterator
    for (auto it = ms[0].rbegin(); it != ms[0].rend(); ++it) {
        auto [cost, earn] = *it;
        
        // Kiểm tra xem có đủ tiền để bắt đầu chuỗi công việc này không
        // init_money + res là tổng tiền hiện có
        // cost là số âm, ví dụ -10. Ta cần tiền >= 10 <=> tiền + (-10) >= 0
        if (init_money + res + cost < 0) {
            // Nếu việc dễ nhất (ưu tiên nhất) không làm được thì các việc khó hơn cũng chịu
            break; 
        }
        res += earn;
    }

    cout << res;

    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...