Submission #1305883

#TimeUsernameProblemLanguageResultExecution timeMemory
1305883orzorzorzFireworks (APIO16_fireworks)C++20
100 / 100
278 ms47196 KiB
/** * APIO 2016 - Fireworks * Solution by Gemini * * Approach: Slope Trick with Leftist Heap * Time Complexity: O((N+M) log(N+M)) */ #include <iostream> #include <vector> #include <algorithm> using namespace std; // Leftist Heap Node struct Node { long long val; int dist; // Distance to null node (for leftist property) Node *l, *r; Node(long long v) : val(v), dist(1), l(nullptr), r(nullptr) {} }; // Merge two leftist heaps Node* merge(Node* a, Node* b) { if (!a) return b; if (!b) return a; // Max-Heap property: larger values on top if (a->val < b->val) swap(a, b); a->r = merge(a->r, b); if (!a->l || (a->r && a->l->dist < a->r->dist)) { swap(a->l, a->r); } a->dist = (a->r ? a->r->dist : 0) + 1; return a; } Node* pop(Node* root) { Node* l = root->l; Node* r = root->r; delete root; // Clean up memory return merge(l, r); } const int MAXN = 300005; vector<pair<int, int>> adj[MAXN]; // Adjacency list: {child, weight} Node* heaps[MAXN]; int N, M; int main() { // Optimize I/O operations ios_base::sync_with_stdio(false); cin.tie(NULL); if (!(cin >> N >> M)) return 0; // The total number of nodes is N + M // Input format: parents for nodes 2 to N+M for (int i = 2; i <= N + M; ++i) { int p; long long w; cin >> p >> w; adj[p].push_back({i, (int)w}); } // Process the tree from bottom up (Post-order traversal) // Since node indices are topological (parents < children usually, but let's do explicit DFS) // Actually, explicit DFS is safer. // We can use a non-recursive approach or recursive. // Given depth, recursive is fine. N+M = 300,000 might risk stack overflow on some systems, // but typically 256MB stack is enough. We'll use recursive DFS. // We need to calculate base cost: sum of all edge weights. // The "slope trick" calculates the reduction/increase relative to a baseline. // Specifically, f(0) = sum(all weights). long long base_cost = 0; // DFS lambda // We can't use std::function efficiently for competitive programming sometimes, // but a manual recursive function is fine. // We'll traverse using a manual stack or just standard recursion. // Let's use a standard recursive function defined outside or strict ordering. // Since input P_i < i is NOT guaranteed, we must use DFS. auto dfs = [&](auto&& self, int u) -> void { // If leaf (Explosive) if (adj[u].empty()) { // Leaf cost function corresponds to slopes -1, 1 breaking at 0. // But we process the edge to parent at the parent's level? // No, the standard slope trick pushes the edge effect at the node. // For a leaf, distance 0 cost is 0. // Breakpoints at 0, 0 (slope changes -1 -> 0 -> 1). heaps[u] = new Node(0); heaps[u] = merge(heaps[u], new Node(0)); return; } for (auto& edge : adj[u]) { int v = edge.first; int w = edge.second; base_cost += w; self(self, v); // Merge child's heap into current heaps[u] = merge(heaps[u], heaps[v]); } // Processing at node u (before passing to parent) // We have merged all children. // Current max slope is roughly equal to degree (number of children). // We need to shape the function for the edge extending upwards from u. // The transformation requires max slope to be 1. // So we pop (degree - 1) largest elements. // Note: For a leaf, we created {0, 0}. Max slope 1. // For internal node u, we merged children. // If u has k children, each child ends with slope 1 (after their processing). // So sum ends with slope k. // We want to reduce slope to 1. So pop k-1 elements. int k = adj[u].size(); for (int i = 0; i < k - 1; ++i) { heaps[u] = pop(heaps[u]); } // Now we apply the edge weight shift. // The transformation: f_new(x) = min_y (f_old(y) + |x - y - w|) // This shifts the "slope 0" and "slope 1" transition points by w. // Top of heap is R (slope 1 transition). // Next is L (slope 0 transition). long long R = heaps[u]->val; heaps[u] = pop(heaps[u]); long long L = heaps[u]->val; heaps[u] = pop(heaps[u]); // Push shifted values // Note: We access the parent edge weight in the caller or here? // The problem is we need the weight of the edge ABOVE u. // But the current logic inside `for` loop handled `base_cost` for edge u->v. // The edge above u is handled when `dfs(parent)` calls `dfs(u)`. // Wait, we need to apply the edge weight of u->parent NOW. // But we don't have it easily. // We should actually handle the "shift" logic using the edge leading TO u. // The problem description gives edges from P to i. // So we need to look up weight of edge parent[u] -> u. // Actually, simpler: pass weight in DFS. }; // Redefine DFS to take edge weight to parent auto dfs_refined = [&](auto&& self, int u, int w_to_parent) -> void { if (adj[u].empty()) { // Leaf // Initial breakpoints 0, 0. heaps[u] = new Node(0); heaps[u] = merge(heaps[u], new Node(0)); } else { for (auto& edge : adj[u]) { int v = edge.first; int w = edge.second; base_cost += w; self(self, v, w); heaps[u] = merge(heaps[u], heaps[v]); } // Pop degree-1 elements int k = adj[u].size(); for (int i = 0; i < k - 1; ++i) { heaps[u] = pop(heaps[u]); } } // Apply edge weight w_to_parent // If u is root (1), w_to_parent is 0 or irrelevant (we don't shift at root). if (u != 1) { long long R = heaps[u]->val; heaps[u] = pop(heaps[u]); long long L = heaps[u]->val; heaps[u] = pop(heaps[u]); heaps[u] = merge(heaps[u], new Node(R + w_to_parent)); heaps[u] = merge(heaps[u], new Node(L + w_to_parent)); } }; dfs_refined(dfs_refined, 1, 0); // Final Calculation at Root // The heap contains change points. // Slope starts at -M (number of leaves). // F(0) = base_cost. // We integrate until slope becomes 0. // The slope becomes 0 after M increments. // Since heap is Max-Heap, it stores the LARGEST change points. // The points where slope is negative are the smallest ones (deep in heap or not popped). // The range of slopes is [-M, M]. Total 2M points. // We want the value at the point where slope becomes 0. // That is the M-th smallest point. // Since we have a Max-Heap of size 2M (roughly), the M-th smallest is the (Size - M + 1)-th largest. // Or more simply: // We just pop elements. Each pop corresponds to a slope segment at the far right. // Slope at far right (after last point) is M. // Pop 1 -> slope M-1. // ... // Pop M -> slope 0. // So we just need to integrate backwards from infinity? // No, standard way: // Sort all points. Calculate from 0. vector<long long> points; while (heaps[1]) { points.push_back(heaps[1]->val); heaps[1] = pop(heaps[1]); } sort(points.begin(), points.end()); // Logic: // f(x) has slope -M at x=0. // f(0) = base_cost. // Points p_0, p_1, ... // Cost at p_0 (first change point): // f(p_0) = f(0) + slope * (p_0 - 0) = base_cost + (-M) * p_0. // New slope = -M + 1. // And so on. // We stop when slope becomes 0. long long current_cost = base_cost; long long current_slope = -M; long long prev_x = 0; for (long long x : points) { current_cost += current_slope * (x - prev_x); current_slope++; prev_x = x; if (current_slope == 0) break; } cout << current_cost << endl; 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...