제출 #602521

#제출 시각아이디문제언어결과실행 시간메모리
602521jophyyjhJob Scheduling (IOI19_job)C++14
100 / 100
243 ms18748 KiB
/**
 * What an interesting pratice contest problem! I could only solve the first 3
 * subtasks. (they're really trivial...) After that I searched online for an answer.
 * 
 * The idea is like "Chain Reactions" (Google Code Jam 2022 Qual.). Since a node can
 * only be chosen when its parent has been, we try to carefully merge leaf nodes into
 * their parent through a greedy approach. Previously, I tried too hard on
 * identifying the first / last selected node, which can be quite difficult when our
 * search base is the whole tree.
 * 
 * Each time, we try to find a node v with the smallest d[]/u[]. (This node need not
 * be a leaf.) We try to merge v into its parent p, i.e. u[p] += u[v], d[p] += d[v],
 * adjust the cost and let children of v have p as their direct parent. Why is
 * this greedy correct?
 * Although it could be difficult to determine which leaf is the last / which node
 * is the first, if v has the smallest d[]/u[], there exists a optimal schedule in
 * which v is built immediately after p is built. Simple argument of "exchange" can
 * prove this. In such case, we can just consider the time diff. (and add the
 * additional cost). Note that all children of v now have p as their direct parent
 * because once v and p are built, all children of v can start.
 * Learnt lots of stuff from this problem!
 * 
 * PS1  The solution above is inspired by Tutis (from Codeforces) in
 *      https://codeforces.com/blog/entry/68632.
 * PS2  When we apply our greedy strategy, the tree may be splited, forming a
 *      forest. We can add a pseudo root (the original -1 in parents[]) to simplify
 *      our code.
 * 
 * Time Complexity: O(n * log(n))
 * Implementation 2     (full solution, inspired by Tutis from Codeforces)
*/

#include <bits/stdc++.h>
#include "job.h"

typedef long long   ll;
typedef std::vector<int>    vec;

// DSU that can assign a value to each set
struct DSU {
    vec parents, rank, values;
    DSU(int n) {
        parents.resize(n);
        rank.resize(n);
        values.resize(n);
        for (int i = 0; i < n; i++)
            parents[i] = i, rank[i] = 1;
    }
    inline int find(int k) {
        if (parents[k] == k)
            return k;
        return parents[k] = find(parents[k]);
    }
    inline int& get_val(int k) {
        k = find(k);
        return values[k];
    }
    bool merge(int a, int b) {      // merge b into a
        a = find(a), b = find(b);
        if (a == b)
            return false;
        int val = values[a];
        if (rank[b] > rank[a])
            std::swap(a, b);
        rank[a] += rank[b], parents[b] = a, values[a] = val;
        return true;
    }
};


struct r_t {
    int node;
    double r;
};

inline bool operator<(const r_t& r1, const r_t& r2)     { return r1.r > r2.r; }

ll scheduling_cost(std::vector<int> _p, std::vector<int> _u, std::vector<int> _d) {
    int n = _p.size();
    std::vector<int> parents(n + 1), u(n + 1), d(n + 1);
    for (int i = 0; i < n; i++)
        parents[i + 1] = _p[i] + 1, u[i + 1] = _u[i], d[i + 1] = _d[i];
    parents[0] = -1, u[0] = d[0] = 0;       // pseudo node

    // Use dsu to maintain merged nodes
    DSU dsu(n + 1);
    dsu.get_val(0) = 0;
    std::vector<double> r(n + 1);
    std::priority_queue<r_t> pq;
    for (int i = 1; i <= n; i++) {
        dsu.get_val(i) = i, r[i] = double(d[i]) / u[i];
        pq.push(r_t{i, r[i]});
    }
    ll cost = 0;
    while (!pq.empty()) {
        r_t rt = pq.top();
        pq.pop();
        if (dsu.get_val(rt.node) != rt.node || r[rt.node] != rt.r)
            continue;
        int v = rt.node, p = dsu.get_val(parents[v]);
        // std::cerr << "(p,v): (" << p << ',' << v << "),\tpairs of (u,d) (" << u[p]
        //           << ',' << d[p] << ") (" << u[v] << ',' << d[v] << ")" << std::endl;
        cost -= ll(d[v]) * ll(u[p]);
        d[p] += d[v], u[p] += u[v], r[p] = double(d[p]) / u[p];
        dsu.merge(p, v);
        if (p > 0)
            pq.push(r_t{p, r[p]});
    }
    cost += ll(u[0]) * ll(d[0]);
    return cost;
}
#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...
#Verdict Execution timeMemoryGrader output
Fetching results...