Submission #1263451

#TimeUsernameProblemLanguageResultExecution timeMemory
1263451garyMin-max tree (BOI18_minmaxtree)C++20
36 / 100
1095 ms23308 KiB
#include <bits/stdc++.h>
using namespace std;

const int INF = 1000000000;

struct Edge {
    int to, id;
};
int n;

// ---------- HLD ----------
vector<vector<Edge>> g;
vector<int> parent, depth, heavy, sz, head, pos;
vector<int> edgeIdAtPos; // maps position -> original edge id (for edge mapped to child node)
int curPos;

int dfs_sz(int v, int p) {
    parent[v] = p;
    sz[v] = 1;
    heavy[v] = -1;
    int maxsz = 0;
    for (auto& e : g[v]) {
        int to = e.to;
        if (to == p) continue;
        depth[to] = depth[v] + 1;
        int s = dfs_sz(to, v);
        if (s > maxsz) {
            maxsz = s;
            heavy[v] = to;
        }
        sz[v] += s;
    }
    return sz[v];
}
void dfs_hld(int v, int h, int pedge_id = -1) {
    head[v] = h;
    pos[v] = ++curPos;
    // map the edge that connects v to parent (if exists) to position pos[v]
    if (pedge_id != -1) edgeIdAtPos[pos[v]] = pedge_id;
    if (heavy[v] != -1) {
        // heavy edge uses same head
        int hid = -1;
        // find edge id of heavy edge
        for (auto& e : g[v])
            if (e.to == heavy[v]) {
                hid = e.id;
                break;
            }
        dfs_hld(heavy[v], h, hid);
    }
    for (auto& e : g[v]) {
        int to = e.to;
        if (to == parent[v] || to == heavy[v]) continue;
        dfs_hld(to, to, e.id);
    }
}

// ---------- Segment tree supporting range chmax ----------
struct SegChMax {
    int n;
    vector<int> st, lazy;
    SegChMax(int _n = 0) { init(_n); }
    void init(int _n) {
        n = 1;
        while (n < _n)
            n <<= 1;
        st.assign(2 * n, -INF);
        lazy.assign(2 * n, -INF);
    }
    void apply(int v, int x) {
        st[v] = max(st[v], x);
        lazy[v] = max(lazy[v], x);
    }
    void push(int v) {
        if (lazy[v] != -INF) {
            apply(v << 1, lazy[v]);
            apply(v << 1 | 1, lazy[v]);
            lazy[v] = -INF;
        }
    }
    void update(int v, int l, int r, int ql, int qr, int x) {
        if (ql > r || qr < l) return;
        if (ql <= l && r <= qr) {
            apply(v, x);
            return;
        }
        push(v);
        int m = (l + r) >> 1;
        update(v << 1, l, m, ql, qr, x);
        update(v << 1 | 1, m + 1, r, ql, qr, x);
        st[v] = min(st[v << 1], st[v << 1 | 1]); // value not really used except propagation: keep something
    }
    void update_range(int l, int r, int x) {
        if (l > r) return;
        update(1, 1, n, l, r, x);
    }
    int query_point(int v, int l, int r, int posq) {
        if (l == r) return st[v];
        push(v);
        int m = (l + r) >> 1;
        if (posq <= m)
            return query_point(v << 1, l, m, posq);
        else
            return query_point(v << 1 | 1, m + 1, r, posq);
    }
    int query_point(int p) { return query_point(1, 1, n, p); }
};

// ---------- Segment tree supporting range chmin ----------
struct SegChMin {
    int n;
    vector<int> st, lazy;
    SegChMin(int _n = 0) { init(_n); }
    void init(int _n) {
        n = 1;
        while (n < _n)
            n <<= 1;
        st.assign(2 * n, INF);
        lazy.assign(2 * n, INF);
    }
    void apply(int v, int x) {
        st[v] = min(st[v], x);
        lazy[v] = min(lazy[v], x);
    }
    void push(int v) {
        if (lazy[v] != INF) {
            apply(v << 1, lazy[v]);
            apply(v << 1 | 1, lazy[v]);
            lazy[v] = INF;
        }
    }
    void update(int v, int l, int r, int ql, int qr, int x) {
        if (ql > r || qr < l) return;
        if (ql <= l && r <= qr) {
            apply(v, x);
            return;
        }
        push(v);
        int m = (l + r) >> 1;
        update(v << 1, l, m, ql, qr, x);
        update(v << 1 | 1, m + 1, r, ql, qr, x);
        st[v] = max(st[v << 1], st[v << 1 | 1]); // not important, kept for structure
    }
    void update_range(int l, int r, int x) {
        if (l > r) return;
        update(1, 1, n, l, r, x);
    }
    int query_point(int v, int l, int r, int posq) {
        if (l == r) return st[v];
        push(v);
        int m = (l + r) >> 1;
        if (posq <= m)
            return query_point(v << 1, l, m, posq);
        else
            return query_point(v << 1 | 1, m + 1, r, posq);
    }
    int query_point(int p) { return query_point(1, 1, n, p); }
};

// HLD wrapper to update edges on path u-v (edges are mapped to nodes except LCA)
void update_path_edges(int u, int v, int x, bool isLowerUpdate, SegChMax& segL, SegChMin& segU) {
    while (head[u] != head[v]) {
        if (depth[head[u]] < depth[head[v]]) swap(u, v);
        int h = head[u];
        // update positions pos[h] .. pos[u] (these are nodes) BUT edges correspond to pos[child], so we update whole segment
        if (isLowerUpdate)
            segL.update_range(pos[h], pos[u], x);
        else
            segU.update_range(pos[h], pos[u], x);
        u = parent[h];
    }
    // now same head
    if (depth[u] < depth[v]) swap(u, v);
    // nodes from v..u, but edges exclude v (the LCA), so update pos[v]+1 .. pos[u]
    if (pos[v] + 1 <= pos[u]) {
        if (isLowerUpdate)
            segL.update_range(pos[v] + 1, pos[u], x);
        else
            segU.update_range(pos[v] + 1, pos[u], x);
    }
}

// collect edge positions on path u-v
void collect_edge_positions_on_path(int u, int v, vector<int>& outPos) {
    while (head[u] != head[v]) {
        if (depth[head[u]] < depth[head[v]]) swap(u, v);
        int h = head[u];
        for (int p = pos[h]; p <= pos[u]; ++p)
            outPos.push_back(p);
        u = parent[h];
    }
    if (depth[u] < depth[v]) swap(u, v);
    for (int p = pos[v] + 1; p <= pos[u]; ++p)
        outPos.push_back(p);
}

// ---------- Hopcroft-Karp ----------
struct HopcroftKarp {
    int nL, nR;
    vector<vector<int>> adj;
    vector<int> dist, pairU, pairV;
    HopcroftKarp(int _nL = 0, int _nR = 0) { init(_nL, _nR); }
    void init(int _nL, int _nR) {
        nL = _nL;
        nR = _nR;
        adj.assign(nL, {});
        pairU.assign(nL, -1);
        pairV.assign(nR, -1);
        dist.assign(nL, 0);
    }
    void addEdge(int u, int v) { adj[u].push_back(v); }
    bool bfs() {
        queue<int> q;
        for (int u = 0; u < nL; u++) {
            if (pairU[u] == -1) {
                dist[u] = 0;
                q.push(u);
            } else
                dist[u] = -1;
        }
        bool reachable = false;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int v : adj[u]) {
                int pu = pairV[v];
                if (pu != -1 && dist[pu] == -1) {
                    dist[pu] = dist[u] + 1;
                    q.push(pu);
                }
                if (pu == -1) reachable = true;
            }
        }
        return reachable;
    }
    bool dfs(int u) {
        for (int v : adj[u]) {
            int pu = pairV[v];
            if (pu == -1 || (dist[pu] == dist[u] + 1 && dfs(pu))) {
                pairU[u] = v;
                pairV[v] = u;
                return true;
            }
        }
        dist[u] = -1;
        return false;
    }
    int maxMatching() {
        int matching = 0;
        while (bfs()) {
            for (int u = 0; u < nL; u++)
                if (pairU[u] == -1)
                    if (dfs(u)) matching++;
        }
        return matching;
    }
};

// ---------- main ----------
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    g.assign(n + 1, {});
    vector<pair<int, int>> edges(n); // 1-based, store input edges for output order (edges[1..n-1])
    for (int i = 1; i <= n - 1; i++) {
        int a, b;
        cin >> a >> b;
        edges[i] = {a, b};
        g[a].push_back({b, i});
        g[b].push_back({a, i});
    }
    parent.assign(n + 1, 0);
    depth.assign(n + 1, 0);
    heavy.assign(n + 1, -1);
    sz.assign(n + 1, 0);
    head.assign(n + 1, 0);
    pos.assign(n + 1, 0);
    edgeIdAtPos.assign(n + 2, -1); // positions 1..n
    curPos = 0;
    depth[1] = 0;
    dfs_sz(1, 0);
    dfs_hld(1, 1, -1);

    // read queries
    int q;
    cin >> q;
    struct Q {
        char type;
        int u, v;
        int x;
        int id;
    };
    vector<Q> queries;
    queries.reserve(q);
    for (int i = 0; i < q; i++) {
        char t;
        int u, v, x;
        cin >> t >> u >> v >> x;
        queries.push_back({t, u, v, x, i});
    }

    // Build segtrees (size = n because pos runs 1..n). Edge positions are pos[child] (not for root)
    SegChMax segL(n + 5);
    SegChMin segU(n + 5);

    // process queries: for each 'm' (lower bound) we do chmax on path edges; for each 'M' (upper bound) chmin on path edges
    for (auto& qu : queries) {
        if (qu.type == 'm') { // min on path = x  -> edges must be >= x and one edge == x  => chmax lower bounds
            update_path_edges(qu.u, qu.v, qu.x, true, segL, segU);
        } else { // 'M' max on path = x -> edges must be <= x and one edge == x => chmin upper bounds
            update_path_edges(qu.u, qu.v, qu.x, false, segL, segU);
        }
    }

    // collect final L and U for each edge (by its pos)
    int mEdges = n - 1;
    vector<int> Lpos(n + 2), Upos(n + 2);
    for (int p = 2; p <= n; ++p) { // pos[root] may be 1; edges are mapped to nodes except root
        Lpos[p] = segL.query_point(p);
        Upos[p] = segU.query_point(p);
    }
    // For safety assign for pos==1 too (unused)
    Lpos[1] = segL.query_point(1);
    Upos[1] = segU.query_point(1);

    // Check consistency: for every edge (pos mapping) L <= U
    bool ok = true;
    for (int p = 2; p <= n; p++) {
        if (Lpos[p] > Upos[p]) {
            ok = false;
            break;
        }
    }
    if (!ok) {
        cout << -1 << '\n';
        return 0;
    }

    // Build adjacency for bipartite matching:
    // left = queries (all q), right = edges (indices 1..mEdges -> we'll remap to 0..mEdges-1)
    HopcroftKarp hk(q, mEdges);
    // For each query, collect positions on path and add an edge to each edge id that can host that query's x
    for (int i = 0; i < q; i++) {
        auto& qu = queries[i];
        vector<int> poslist;
        collect_edge_positions_on_path(qu.u, qu.v, poslist);
        for (int p : poslist) {
            if (p <= 0 || p > n) continue;
            int l = Lpos[p], uval = Upos[p];
            if (l <= qu.x && qu.x <= uval) {
                int eId = edgeIdAtPos[p]; // original edge id (1..n-1)
                if (eId == -1) continue;
                hk.addEdge(i, eId - 1); // map edge id to 0-based
            }
        }
    }

    int matching = hk.maxMatching();
    if (matching < q) {
        cout << -1 << '\n';
        return 0;
    }

    // construct weights for edges
    vector<int> weight(n, 1000000000);
    // for each matched right vertex (edge), hk.pairV[edge] gives query id
    for (int e = 0; e < mEdges; e++) {
        int qid = hk.pairV[e];
        int pos_of_edge = -1;
        // find pos for this edge id: we have edgeIdAtPos[pos] = e+1
        // Instead of searching, build reverse map:
        // Let's build reverse map once:
    }
    // build reverse map pos -> edgeId already edgeIdAtPos; let's build edgeId -> pos
    vector<int> edgePos(mEdges + 1, -1); // 1..mEdges
    for (int p = 1; p <= n; p++) {
        int eid = edgeIdAtPos[p];
        if (eid != -1) edgePos[eid] = p;
    }
    // assign weights for matched edges
    for (int e = 1; e <= mEdges; e++) {
        int matchedQuery = hk.pairV[e - 1];
        int p = edgePos[e];
        if (matchedQuery != -1) {
            weight[e] = queries[matchedQuery].x;
        } else {
            // choose a value inside [Lpos[p], Upos[p]]
            int chosen;
            if (Lpos[p] != -INF)
                chosen = Lpos[p];
            else if (Upos[p] != INF)
                chosen = Upos[p];
            else
                chosen = 1000000000;
            // ensure chosen in interval
            if (chosen < Lpos[p]) chosen = Lpos[p];
            if (chosen > Upos[p]) chosen = Upos[p];
            weight[e] = chosen;
        }
    }

    // print edges in original order: a b weight
    for (int i = 1; i <= mEdges; i++) {
        cout << edges[i].first << ' ' << edges[i].second << ' ' << weight[i] << '\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...