제출 #1350849

#제출 시각아이디문제언어결과실행 시간메모리
1350849NotLinuxJobs (BOI24_jobs)C++20
0 / 100
140 ms34284 KiB
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

struct DSU {
    vector<int> p;
    DSU(int n = 0) { init(n); }
    void init(int n) {
        p.resize(n + 1);
        iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        if (p[x] == x) return x;
        return p[x] = find(p[x]);
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    ll s;
    cin >> N >> s;

    DSU dsu(N);

    vector<int> parentOf(N + 1, -1);   // parent component pointer; current parent = find(parentOf[x])
    vector<ll> need(N + 1, 0), gain(N + 1, 0);
    vector<int> ver(N + 1, 0);

    // child heaps: for each live component p, candidates for profitable current children of p
    using ChildEntry = tuple<ll, int, int>; // (need_snapshot, child_id, child_version)
    vector<priority_queue<ChildEntry, vector<ChildEntry>, greater<ChildEntry>>> childHeap(N + 1);

    // global profitable blocks eligible for HARD merge:
    // current parent must exist and must not be fake root 0
    priority_queue<ChildEntry, vector<ChildEntry>, greater<ChildEntry>> hardHeap;

    // global possible EASY merges:
    // delta = child.need - (parent.need + parent.gain)
    using EasyEntry = tuple<ll, int, int, int, int>; // (delta, p, verP, c, verC)
    priority_queue<EasyEntry, vector<EasyEntry>, greater<EasyEntry>> easyHeap;

    auto getParent = [&](int x) -> int {
        if (parentOf[x] == -1) return -1;
        return dsu.find(parentOf[x]);
    };

    // fake root 0
    parentOf[0] = -1;
    need[0] = 0;
    gain[0] = s;

    for (int i = 1; i <= N; i++) {
        ll x;
        int p;
        cin >> x >> p;
        gain[i] = x;
        need[i] = max<ll>(0, -x);
        parentOf[i] = p; // parent in initial tree, with fake root 0
    }

    auto isHardEligible = [&](int x) -> bool {
        int p = getParent(x);
        return p != -1 && p != 0;
    };

    auto cleanChildren = [&](int p) {
        p = dsu.find(p);
        auto &pq = childHeap[p];
        while (!pq.empty()) {
            auto [snapNeed, c, snapVer] = pq.top();
            int rc = dsu.find(c);
            if (rc != c) {
                pq.pop();
                continue;
            }
            if (ver[c] != snapVer) {
                pq.pop();
                continue;
            }
            if (gain[c] < 0) {
                pq.pop();
                continue;
            }
            if (getParent(c) != p) {
                pq.pop();
                continue;
            }
            break;
        }
    };

    auto refreshParent = [&](int p) {
        p = dsu.find(p);
        cleanChildren(p);
        if (!childHeap[p].empty()) {
            auto [snapNeed, c, snapVer] = childHeap[p].top();
            ll delta = need[c] - (need[p] + gain[p]);
            easyHeap.push({delta, p, ver[p], c, ver[c]});
        }
    };

    auto pushProfitableBlock = [&](int x) {
        x = dsu.find(x);
        if (gain[x] < 0) return;

        int p = getParent(x);
        if (p != -1) {
            childHeap[p].push({need[x], x, ver[x]});
        }
        if (isHardEligible(x)) {
            hardHeap.push({need[x], x, ver[x]});
        }
    };

    for (int i = 1; i <= N; i++) {
        if (gain[i] >= 0) pushProfitableBlock(i);
    }
    for (int i = 0; i <= N; i++) refreshParent(i);

    auto meldHeaps = [&](int a, int b) {
        if (childHeap[a].size() < childHeap[b].size()) swap(a, b);
        while (!childHeap[b].empty()) {
            childHeap[a].push(childHeap[b].top());
            childHeap[b].pop();
        }
        return pair<int, int>{a, b}; // merged into a, b becomes dead
    };

    auto validEasyTop = [&]() -> optional<pair<int,int>> {
        while (!easyHeap.empty()) {
            auto [delta, p, vp, c, vc] = easyHeap.top();

            p = dsu.find(p);
            if (p != get<1>(easyHeap.top())) {
                easyHeap.pop();
                continue;
            }
            if (ver[p] != vp) {
                easyHeap.pop();
                continue;
            }

            cleanChildren(p);
            if (childHeap[p].empty()) {
                easyHeap.pop();
                continue;
            }

            auto [snapNeed, realC, realVC] = childHeap[p].top();
            ll realDelta = need[realC] - (need[p] + gain[p]);

            if (realC != c || realVC != vc || realDelta != delta) {
                easyHeap.pop();
                continue;
            }

            return pair<int,int>{p, c};
        }
        return nullopt;
    };

    auto validHardTop = [&]() -> optional<int> {
        while (!hardHeap.empty()) {
            auto [snapNeed, x, vx] = hardHeap.top();
            x = dsu.find(x);
            if (x != get<1>(hardHeap.top())) {
                hardHeap.pop();
                continue;
            }
            if (ver[x] != vx) {
                hardHeap.pop();
                continue;
            }
            if (gain[x] < 0) {
                hardHeap.pop();
                continue;
            }
            if (!isHardEligible(x)) {
                hardHeap.pop();
                continue;
            }
            return x;
        }
        return nullopt;
    };

    auto mergeBlocks = [&](int p, int c, bool easy) {
        p = dsu.find(p);
        c = dsu.find(c);

        // Current parent of c must be p
        int gp = getParent(p); // parent of the merged block after contraction

        ll newGain = gain[p] + gain[c];
        ll newNeed;
        if (easy) {
            // easy case implies max(need[p], need[c] - gain[p]) = need[p]
            newNeed = need[p];
        } else {
            // hard case implies the active branch is need[c] - gain[p]
            newNeed = need[c] - gain[p];
        }

        // small-to-large on child heaps; representative can be either p or c
        int rep, dead;
        tie(rep, dead) = meldHeaps(p, c);

        dsu.p[dead] = rep;

        if (rep == c) {
            // merged component replaces parent p in the tree
            parentOf[rep] = gp;
        }
        // if rep == p, parentOf[p] already equals gp

        gain[rep] = newGain;
        need[rep] = newNeed;

        ver[rep]++;
        ver[dead]++; // invalidate stale entries mentioning dead

        if (gain[rep] >= 0) {
            int par = getParent(rep);
            if (par != -1) {
                childHeap[par].push({need[rep], rep, ver[rep]});
                refreshParent(par);
            }
            if (isHardEligible(rep)) {
                hardHeap.push({need[rep], rep, ver[rep]});
            }
        }

        refreshParent(rep);
        if (gp != -1) refreshParent(gp);
    };

    while (true) {
        // Exhaust all easy merges first
        while (true) {
            auto cand = validEasyTop();
            if (!cand.has_value()) break;

            int p = cand->first;
            int c = cand->second;
            ll delta = need[c] - (need[p] + gain[p]);
            if (delta > 0) break;

            mergeBlocks(p, c, true);
        }

        auto hard = validHardTop();
        if (!hard.has_value()) break;

        int x = *hard;
        int p = getParent(x);
        mergeBlocks(p, x, false);
    }

    cout << gain[dsu.find(0)] << '\n';
    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...