Submission #1263191

#TimeUsernameProblemLanguageResultExecution timeMemory
1263191amigoo99234Harbingers (CEOI09_harbingers)C++20
50 / 100
1093 ms48460 KiB
#include <bits/stdc++.h>

using namespace std;

//long long MOD = 1e9 + 7;
const long long MOD = 998244353;
// const long long MOD = 100000007 ;


#define fast ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)

#define popCnt(x) (__builtin_popcountll(x))
#define int long long
#define ld long double

#define F  first
#define S  second
#define pi  M_PI
#define all(x) x.begin() , x.end()
#define ll long long
#define SZ(v) (int)(v.size())

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
const long long OO = LLONG_MAX / 4, N = 3e5 + 5, M = 1e5 + 5, K = 505;

// rollback-capable LineContainer
struct Line {
    mutable ll k, m, p;
    bool operator<(const Line &o) const { return k < o.k; }
    bool operator<(ll x) const { return p < x; }
};

struct RollbackLineContainer : multiset<Line, less<>> {
    static const ll inf = LLONG_MAX;
    bool isMn = true;

    // low-level op types for rollback logging
    enum OpType { OP_INSERT, OP_ERASE, OP_SETP };
    struct Op {
        OpType type;
        Line ln; // a copy of the affected line (stores old p for SETP)
    };

    // history of operations (each add produces a vector<Op>)
    vector<vector<Op>> history;

    // integer division floor for negatives like in original code
    ll divll(ll a, ll b) {
        return a / b - ((a ^ b) < 0 && a % b);
    }

    // helper: set p of an element (Line::p is mutable so can be changed even though element is "const")
    void set_p(iterator it, ll newp) {
        // mutate the mutable member
        const_cast<ll&>(it->p) = newp;
    }

    // helper: find an element with exactly same (k,m). returns end() if not found.
    iterator find_same(const Line &target) {
        // use lower_bound(Line) which compares by k (operator<(Line))
        auto it = lower_bound(target);
        for (; it != end() && it->k == target.k; ++it) {
            if (it->m == target.m) return it;
        }
        return end();
    }

    // erase with logging: log the erased copy then erase and return iterator to next element
    iterator erase_with_log(iterator it, vector<Op> &ops) {
        ops.push_back({OP_ERASE, *it});      // save copy before erasing
        return erase(it);
    }

    // isect with logging: will record a SETP op (old copy) before changing x->p
    // returns same boolean as original isect
    bool isect_log(iterator x, iterator y, vector<Op> &ops) {
        if (y == end()) {
            if (x->p != inf) ops.push_back({OP_SETP, *x});
            set_p(x, inf);
            return false;
        }
        if (x->k == y->k) {
            ll newp = (x->m > y->m ? inf : -inf);
            if (x->p != newp) ops.push_back({OP_SETP, *x});
            set_p(x, newp);
        } else {
            ll newp = divll(y->m - x->m, x->k - y->k);
            if (x->p != newp) ops.push_back({OP_SETP, *x});
            set_p(x, newp);
        }
        return x->p >= y->p;
    }

    // add as before but log all low-level changes into a vector<Op> and push it to history
    void add(ll k, ll m) {
        vector<Op> ops; ops.reserve(8); // small reservation to reduce reallocation

        if (isMn) { k = -k; m = -m; }

        // insert new line (p will be fixed by isect calls)
        auto z = insert({k, m, 0});
        ops.push_back({OP_INSERT, *z}); // remember insertion (copy)

        auto y = z++;
        auto x = y;

        while (isect_log(y, z, ops)) z = erase_with_log(z, ops);

        if (x != begin() && isect_log(--x, y, ops)) {
            // if isect(--x,y) true then we must erase y and re-isect the new predecessor
            y = erase_with_log(y, ops);
            isect_log(x, y, ops);
        }

        while ((y = x) != begin()) {
            --x;
            if (x->p >= y->p) {
                // erase y (log it) and re-evaluate intersection on x
                auto nxt = erase_with_log(y, ops);
                isect_log(x, nxt, ops);
            } else break;
        }

        history.push_back(move(ops));
    }

    // query unchanged
    ll query(ll x) {
        auto l = *lower_bound(x);
        return (isMn ? -1 : 1) * (l.k * x + l.m);
    }

    // rollback the last add (revert logged ops in reverse order)
    void rollback() {
        if (history.empty()) return;
        auto ops = move(history.back());
        history.pop_back();

        // revert in reverse order
        for (auto it = ops.rbegin(); it != ops.rend(); ++it) {
            switch (it->type) {
                case OP_ERASE:
                    // inverse of erase is insert the stored copy back
                    insert(it->ln);
                    break;
                case OP_SETP: {
                    // inverse of set-p: find the element with same (k,m) and restore old p
                    auto found = find_same(it->ln);
                    if (found != end()) set_p(found, it->ln.p);
                    break;
                }
                case OP_INSERT: {
                    // inverse of insert is remove the inserted line (find by k,m)
                    auto found = find_same(it->ln);
                    if (found != end()) erase(found);
                    break;
                }
            }
        }
    }

    // rollback last t adds (if t > history.size() we rollback everything)
    void rollback_n(size_t t) {
        while (t-- && !history.empty()) rollback();
    }

    // clear history (forget ability to rollback)
    void clear_history() {
        history.clear();
    }
};


RollbackLineContainer cht;
int n;
vector<int> v, c;
vector<vector<pair<int, int>>> adj;

vector<int> dp;
void dfs(int node , int p = -1 , int d = 0)
{
    if(p == -1) dp[node] = 0;
    else dp[node] = cht.query(v[node]) + v[node] * d + c[node];

    cht.add(-d , dp[node]);

    for(auto [child , len] : adj[node])
    {
        if(child == p)continue;
        dfs(child , node , d + len);
    }
    cht.rollback();
}

signed main() {
    fast;


//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);

    cin >> n;
    dp = v = c = vector<int>(n);
    adj.resize(n);

    for (int i = 0; i < n - 1; ++i) {
        int u, vv, d;
        cin >> u >> vv >> d;
        u--;
        vv--;
        adj[u].emplace_back(vv, d);
        adj[vv].emplace_back(u, d);
    }

    for (int i = 1; i < n; ++i) {
        cin >> c[i] >> v[i];
    }


    dfs(0);

    for(int i = 1 ; i < n ; i++) cout << dp[i] << " ";
    cout<<'\n';



}
#Verdict Execution timeMemoryGrader output
Fetching results...