Hướng dẫn giải của Điều Quân


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
// AI_SOL.cpp — Independent correct solution (O(n), full problem).
//
// Serves as the differential oracle for all four subtasks. Correctness was
// verified against a BFS brute oracle on 36000+ exhaustive small cases plus
// random tests; runs n = 2e5 in <0.1s.
//
// Approach (see editorial): block-cut decomposition, bottom-up.
//   -1  iff sum(a) != sum(b).
//   Bridge (p, w), p = parent articulation: cost += |flow| where flow = subtree
//     imbalance of w; pass flow up to p.
//   Cycle, parent articulation p = v1, other vertices v2..vk: demands d_i =
//     subtree imbalance of v_i (NON-parent only; p's own g is handled at p's
//     level). cost += sum |T_i - median(T)| where T = {0, d_2, d_2+d_3, ...}
//     (k prefix sums, one per cycle edge); pass sum(d_i) up to p.
//   Both the BCC computation and the block-cut DP are ITERATIVE to avoid
//   stack overflow on deep inputs (e.g. a path of length 2e5). Uses long long.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, m;
vector<vector<int>> adj;
vector<int> g;                       // a_i - b_i

// --- iterative Tarjan edge-BCC ---
vector<int> num, low;
vector<pair<int,int>> estk;
vector<vector<pair<int,int>>> block_edges;   // edges per BCC
vector<bool> isArt;
vector<vector<int>> blocksOf;                // vertex -> block ids

void computeBCC() {
    num.assign(n, 0); low.assign(n, 0);
    estk.clear(); block_edges.clear();
    int cnt = 0;
    struct Fr { int u, p, idx; };
    vector<Fr> stk;
    for (int s = 0; s < n; ++s) {
        if (num[s]) continue;
        num[s] = low[s] = ++cnt;
        stk.push_back({s, -1, 0});
        while (!stk.empty()) {
            Fr &f = stk.back();
            int u = f.u, p = f.p;
            if (f.idx < (int)adj[u].size()) {
                int v = adj[u][f.idx++];
                if (v == p) { /* skip one tree edge back to parent (graph is simple) */ }
                else if (!num[v]) {
                    estk.push_back({u, v});
                    num[v] = low[v] = ++cnt;
                    stk.push_back({v, u, 0});
                } else if (num[v] < num[u]) {
                    estk.push_back({u, v});
                    low[u] = min(low[u], num[v]);
                }
            } else {
                stk.pop_back();
                if (!stk.empty()) {
                    int pu = stk.back().u;
                    low[pu] = min(low[pu], low[u]);
                    if (low[u] >= num[pu]) {
                        vector<pair<int,int>> b; pair<int,int> e;
                        do {
                            e = estk.back(); estk.pop_back(); b.push_back(e);
                        } while (!(e.first == pu && e.second == u));
                        block_edges.push_back(b);
                    }
                }
            }
        }
    }
    // articulation points (standard, iterative)
    isArt.assign(n, false);
    {
        vector<int> n2(n, 0), l2(n, 0); int c2 = 0;
        struct Fr2 { int u, p, idx; }; vector<Fr2> st;
        for (int s = 0; s < n; ++s) {
            if (n2[s]) continue;
            n2[s] = l2[s] = ++c2; int ch = 0; int par = -1;
            st.push_back({s, -1, 0});
            // iterative DFS that tracks children count per node
            vector<int> parOf(n, -1), childCount(n, 0);
            while (!st.empty()) {
                Fr2 &fr = st.back();
                int u = fr.u;
                if (fr.idx < (int)adj[u].size()) {
                    int v = adj[u][fr.idx++];
                    if (v == fr.p) continue;
                    if (!n2[v]) {
                        parOf[v] = u;
                        n2[v] = l2[v] = ++c2;
                        ++childCount[u];
                        st.push_back({v, u, 0});
                    } else if (n2[v] < n2[u]) {
                        l2[u] = min(l2[u], n2[v]);
                    }
                } else {
                    st.pop_back();
                    if (parOf[u] != -1) {
                        int pu = parOf[u];
                        l2[pu] = min(l2[pu], l2[u]);
                        if (l2[u] >= n2[pu] && parOf[pu] != -1) isArt[pu] = true;
                    }
                }
            }
            if (childCount[s] >= 2) isArt[s] = true;
        }
    }
    // blocksOf
    blocksOf.assign(n, {});
    for (int b = 0; b < (int)block_edges.size(); ++b) {
        set<int> vs;
        for (auto &e : block_edges[b]) { vs.insert(e.first); vs.insert(e.second); }
        for (int v : vs) blocksOf[v].push_back(b);
    }
}

// cycle vertex order starting at parent p, excluding p at the end
vector<int> cycleOrder(int b, int p) {
    map<int, vector<int>> badj;
    for (auto &e : block_edges[b]) { badj[e.first].push_back(e.second); badj[e.second].push_back(e.first); }
    vector<int> order = {p};
    int prev = -1, cur = p;
    while (true) {
        int nxt = -1;
        for (int x : badj[cur]) if (x != prev) { nxt = x; break; }
        if (nxt == -1 || nxt == p) break;
        order.push_back(nxt);
        prev = cur; cur = nxt;
    }
    return order;
}

ll circulationCost(const vector<ll>& d) {
    int k = (int)d.size() + 1;          // k edges => k prefix sums
    vector<ll> T(k); T[0] = 0;
    for (int i = 1; i < k; ++i) T[i] = T[i-1] + d[i-1];
    sort(T.begin(), T.end());
    ll med = T[k/2], c = 0;
    for (ll x : T) c += llabs(x - med);
    return c;
}

ll totalCost;

ll solve() {
    ll s = 0; for (int x : g) s += x;
    if (s != 0) return -1;

    computeBCC();
    int B = block_edges.size();

    // Per-block vertex set.
    vector<vector<int>> blockVerts(B);
    for (int b = 0; b < B; ++b) {
        set<int> vs;
        for (auto &e : block_edges[b]) { vs.insert(e.first); vs.insert(e.second); }
        blockVerts[b] = vector<int>(vs.begin(), vs.end());
    }

    // Iterative DFS over the block-cut tree from vertex 0, recording parent
    // pointers and a post-order of vertex nodes. Children of a vertex u are its
    // blocks (except its parent block); children of a block b are its vertices
    // (except its parent articulation). Bipartite => each non-root vertex is
    // reached through exactly one block, so parent pointers are set once.
    vector<int> blockPar(B, -1);        // parent articulation of block b
    vector<int> vertexParBlk(n, -1);    // parent block of vertex u (-1 for root)
    vector<int> post;
    struct St { int u; };               // vertex nodes only; we drop block markers
    vector<St> st;
    vector<char> visV(n, 0);
    st.push_back({0}); visV[0] = 1;
    while (!st.empty()) {
        int u = st.back().u; st.pop_back();
        post.push_back(u);
        for (int b : blocksOf[u]) {
            if (b == vertexParBlk[u]) continue;       // parent block
            blockPar[b] = u;
            for (int w : blockVerts[b]) {
                if (w == u) continue;
                if (!visV[w]) { visV[w] = 1; vertexParBlk[w] = b; st.push_back({w}); }
            }
        }
    }
    // post is currently a pre-order; reverse for bottom-up processing.
    vector<int> vpost(post.rbegin(), post.rend());

    // For each cycle block, the non-parent vertices in CYCLE order (consecutive
    // around the ring), with their position index. circulationCost requires the
    // prefix sums to be along consecutive cycle edges, so order matters.
    // The parent articulation is blockPar[b]; cycleOrder walks from it.
    // Each non-root vertex has exactly one parent block, so its position within
    // that block's cycle order is stored as a single int per vertex.
    vector<int> cycleNonParCount(B, 0);   // #non-parent vertices per cycle block
    vector<int> posInParBlk(n, -1);        // position of u within its parent block's cycle order
    for (int b = 0; b < B; ++b) {
        if ((int)block_edges[b].size() == 1) continue;   // bridge
        int p = blockPar[b];
        vector<int> ord = cycleOrder(b, p);     // [p, v2, ..., vk]
        int idx = 0;
        for (int i = 1; i < (int)ord.size(); ++i) {
            posInParBlk[ord[i]] = idx++;        // vertex ord[i] sits at position idx
        }
        cycleNonParCount[b] = (int)ord.size() - 1;
    }

    // Bottom-up DP.
    vector<ll> subV(n, 0);               // subtree imbalance of vertex u
    vector<ll> blkFlow(B, 0);            // imbalance passed up by block b
    vector<vector<ll>> dem(B);           // buffered demands, indexed by cycle position
    vector<int> demSeen(B, 0);
    totalCost = 0;
    for (int u : vpost) {
        subV[u] = g[u];
        for (int b : blocksOf[u])
            if (b != vertexParBlk[u]) subV[u] += blkFlow[b];
        int pb = vertexParBlk[u];
        if (pb == -1) continue;          // root
        if ((int)block_edges[pb].size() == 1) {
            // bridge (blockPar, u): forced flow = subV[u]
            totalCost += llabs(subV[u]);
            blkFlow[pb] += subV[u];
        } else {
            // cycle: place this vertex's demand at its cycle position
            int pos = posInParBlk[u];
            if ((int)dem[pb].size() <= pos) dem[pb].resize(pos + 1);
            dem[pb][pos] = subV[u];
            if (++demSeen[pb] == cycleNonParCount[pb]) {
                totalCost += circulationCost(dem[pb]);
                for (ll x : dem[pb]) blkFlow[pb] += x;
            }
        }
    }
    return totalCost;
}

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr);
    int t; if (!(cin >> t)) return 0;
    while (t--) {
        cin >> n >> m;
        vector<int> a(n), b(n);
        for (int i = 0; i < n; ++i) cin >> a[i];
        for (int i = 0; i < n; ++i) cin >> b[i];
        g.assign(n, 0);
        for (int i = 0; i < n; ++i) g[i] = a[i] - b[i];
        adj.assign(n, {});
        for (int i = 0; i < m; ++i) {
            int u, v; cin >> u >> v; --u; --v;
            adj[u].push_back(v); adj[v].push_back(u);
        }
        cout << solve() << '\n';
    }
    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.