Submission #1305884

#TimeUsernameProblemLanguageResultExecution timeMemory
1305884orzorzorzFireworks (APIO16_fireworks)C++20
0 / 100
5 ms9788 KiB
/**
 * APIO 2016 - Fireworks
 * 55+ Point Solution (STL Priority Queue + Small-to-Large Merging)
 */

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

const int MAXN = 300005;

// Adjacency list: store children indices
vector<int> adj[MAXN];
// Edge weights: weight[i] is weight of edge between i and its parent
long long edge_weight[MAXN];

// Priority Queue to store breakpoints for each node
priority_queue<long long> pq[MAXN];

// Number of leaves in the subtree
int num_leaves[MAXN];

void dfs(int u) {
    // Base Case: If u is a leaf (no children)
    if (adj[u].empty()) {
        num_leaves[u] = 1;
        // Leaf cost function is |x - 0|, breakpoints at 0, 0
        // Slope changes from -1 to 0 at 0, and 0 to 1 at 0
        pq[u].push(0);
        pq[u].push(0);
        return;
    }

    num_leaves[u] = 0;

    for (int v : adj[u]) {
        dfs(v);
        num_leaves[u] += num_leaves[v];

        // Small-to-Large Merging
        // Always merge the smaller queue into the larger one to keep complexity low
        if (pq[u].size() < pq[v].size()) {
            swap(pq[u], pq[v]);
        }
        
        // Move all elements from child v to parent u
        while (!pq[v].empty()) {
            pq[u].push(pq[v].top());
            pq[v].pop();
        }
    }

    // Logic for Internal Nodes (excluding Root for a moment)
    // We want the slope to eventually reach 1.
    // The slope starts at -num_leaves[u].
    // Each breakpoint increases slope by 1.
    // To stop at slope 1, we need (num_leaves[u] + 1) breakpoints.
    // Pop the largest elements (steepest positive slopes) until size fits.
    
    // Note: We perform the edge shift logic ONLY if we are NOT the root yet,
    // or we treat the root specially afterwards. 
    // The standard Slope Trick usually shifts at the child before merging, 
    // or shifts at the node before passing up. Here we process node u completely.

    // To process the edge above u (connecting u to parent):
    // 1. Reduce max slope to 1.
    while (pq[u].size() > num_leaves[u] + 1) {
        pq[u].pop();
    }

    // 2. Pop top two points (L and R), shift them by edge weight W, push back.
    long long r = pq[u].top(); pq[u].pop();
    long long l = pq[u].top(); pq[u].pop();

    pq[u].push(l + edge_weight[u]);
    pq[u].push(r + edge_weight[u]);
}

int main() {
    // Optimize I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;
    if (!(cin >> n >> m)) return 0;

    long long total_edge_sum = 0;

    // Input format: For i from 2 to N+M
    // P_i (Parent), W_i (Weight)
    for (int i = 2; i <= n + m; i++) {
        int p;
        long long w;
        cin >> p >> w;
        adj[p].push_back(i);
        edge_weight[i] = w;
        total_edge_sum += w;
    }

    // Run DFS from the switch (Node 1)
    // Note: The DFS above adds the edge weight of 'u' at the end of 'u' processing.
    // However, Node 1 (Root) has no parent edge.
    // We need to be careful. The logic inside DFS adds edge_weight[u].
    // For root, edge_weight[1] is 0 (global array init), so adding 0 doesn't hurt logic
    // BUT root requires slope 0, not 1.
    
    // Let's adjust DFS slightly or handle root separately.
    // Simpler: Run DFS for children of root, merge into root, then handle root logic manually.
    
    // Re-implementation of Main Loop for Root logic correctness:
    // We will call a specific DFS that handles the shift internally.
    
    // Let's modify the DFS call slightly to be pure.
    // We can just use the DFS function defined above, but for the Root (Node 1),
    // we must undo the "slope 1" logic because Root needs slope 0.
    // Actually, simpler to just implement the logic inside DFS with a check.
    
    // Let's rewrite the DFS loop in main to be explicit:
    for (int v : adj[1]) {
        dfs(v);
        num_leaves[1] += num_leaves[v];
        
        if (pq[1].size() < pq[v].size()) swap(pq[1], pq[v]);
        while (!pq[v].empty()) {
            pq[1].push(pq[v].top());
            pq[v].pop();
        }
    }

    // Final processing for Root (Node 1)
    // Root needs to end at slope 0 (optimality condition).
    // Start slope is -num_leaves[1].
    // Target slope is 0.
    // We need num_leaves[1] breakpoints.
    
    while (pq[1].size() > num_leaves[1]) {
        pq[1].pop();
    }

    // Calculate Answer
    // Min Cost = Sum of all edge weights - Sum of retained breakpoints
    // (This is a derived property of the slope trick for this specific problem)
    
    long long retained_sum = 0;
    while (!pq[1].empty()) {
        retained_sum += pq[1].top();
        pq[1].pop();
    }

    cout << total_edge_sum - retained_sum << 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...