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.
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