結果

問題 No.1265 Balloon Survival
ユーザー qwewe
提出日時 2025-05-14 13:15:32
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 607 ms / 2,000 ms
コード長 12,510 bytes
コンパイル時間 1,332 ms
コンパイル使用メモリ 128,060 KB
実行使用メモリ 19,348 KB
最終ジャッジ日時 2025-05-14 13:16:37
合計ジャッジ時間 7,659 ms
ジャッジサーバーID
(参考情報)
judge4 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 4
other AC * 32
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
#include <set>
#include <vector> 
#include <queue>
#include <limits>
#include <numeric> 
#include <algorithm> // For min/max
#include <map> // Use map to track active status efficiently

using namespace std;

// Use long long for coordinates and squared distances to avoid potential overflow
// Coordinate values can be up to 10^8, squared differences up to (2*10^8)^2 = 4*10^16.
// Sum of two such squares can be up to 8*10^16, which fits within standard 64-bit long long (~9*10^18).
typedef long long ll;

// Structure to represent balloon coordinates
struct Point {
    ll x, y;
};

// Calculate squared Euclidean distance between two points
// This value is proportional to the square of the collision time (specifically distSq = 4 * time^2)
// Comparing distSq is equivalent to comparing collision times.
ll distSq(Point p1, Point p2) {
    ll dx = p1.x - p2.x;
    ll dy = p1.y - p2.y;
    return dx * dx + dy * dy;
}

// Structure to represent a potential collision event between two balloons
struct Collision {
    ll timeSq; // Use squared distance as a proxy for time squared
    int u, v;  // Indices of the two balloons involved

    // Overload greater-than operator for min-heap functionality
    // Prioritize collisions that happen earlier (smaller timeSq)
    bool operator>(const Collision& other) const {
        if (timeSq != other.timeSq) {
            return timeSq > other.timeSq;
        }
        // If collision times are equal, tie-break using balloon indices for consistent ordering.
        // Sort pairs lexicographically: first by the smaller index, then by the larger index.
        int min_this = min(u, v);
        int max_this = max(u, v);
        int min_other = min(other.u, other.v);
        int max_other = max(other.u, other.v);
        if (min_this != min_other) {
             return min_this > min_other;
        }
         return max_this > max_other; // If smaller indices are same, compare larger indices
    }
};


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

    int N; // Number of balloons
    cin >> N;

    vector<Point> balloons(N); // Store balloon coordinates
    for (int i = 0; i < N; ++i) {
        cin >> balloons[i].x >> balloons[i].y;
    }

    // Base case: If there's only one balloon initially, it's already the sole survivor.
    if (N == 1) {
        cout << 0 << endl;
        return 0;
    }

    set<int> removed_indices; // Set to store indices of balloons removed initially

    // The core strategy is iterative: Simulate the process. If balloon 1 bursts,
    // identify the balloon it collided with (culprit), add the culprit to the removed set,
    // and re-simulate. Repeat until balloon 1 is the sole survivor.
    while (true) {
        
        // Use a map to track the active status of balloons for the current simulation run.
        // A map is efficient here as the set of active balloons changes.
        map<int, bool> current_active_status_map; 
        int active_count = 0; // Count of currently active balloons
        vector<int> current_active_indices; // Store indices of active balloons to easily iterate pairs
        
        // Initialize active balloons for this simulation run
        for (int i = 0; i < N; ++i) {
            // A balloon is active if it's not in the `removed_indices` set
            if (removed_indices.find(i) == removed_indices.end()) {
                current_active_status_map[i] = true; // Mark as active
                active_count++;
                current_active_indices.push_back(i);
            }
        }

        // Check initial state for termination conditions
        if (active_count <= 1) {
             // If only 1 balloon is active and it's balloon 0 (the target balloon)
             if (active_count == 1 && current_active_status_map.count(0) && current_active_status_map[0]) {
                 break; // Success condition met, exit the loop.
             } else {
                 // If 0 or 1 balloon active, but it's not balloon 0, then balloon 0 must have been removed.
                 // This implies an issue or edge case. The main logic should handle this by identifying culprits.
                 // Assuming this state means failure is implicitly handled. Break as a fallback.
                 break; 
             }
        }

        // Min-priority queue to manage potential collision events, ordered by time
        priority_queue<Collision, vector<Collision>, greater<Collision>> pq;
        
        // Populate the priority queue with all potential collisions between initially active balloons
        for (size_t i = 0; i < current_active_indices.size(); ++i) {
             for (size_t j = i + 1; j < current_active_indices.size(); ++j) {
                int u = current_active_indices[i];
                int v = current_active_indices[j];
                // Add collision event to PQ. Using distSq as proxy for time.
                pq.push({distSq(balloons[u], balloons[v]), u, v});
            }
        }
        
        bool balloon1_burst = false; // Flag set if balloon 1 bursts during the simulation
        int culprit = -1; // Stores the index of the balloon that collided with balloon 1

        // Simulate the balloon bursting process step-by-step
        while (active_count > 1) {
             // If the priority queue is empty, no more collisions can occur
             if (pq.empty()) { 
                 break; // Stop the simulation for this configuration
             }

            Collision current_collision;
            
             // Find the next valid collision event from the PQ.
             // An event is valid only if both involved balloons are still active.
             while (!pq.empty()) {
                current_collision = pq.top();
                // Check active status using the map
                if (current_active_status_map.count(current_collision.u) && current_active_status_map[current_collision.u] &&
                    current_active_status_map.count(current_collision.v) && current_active_status_map[current_collision.v]) {
                    break; // Found a valid collision event
                }
                // If event is invalid (involves already burst balloon), discard it
                pq.pop();
             }
            
             // If PQ became empty while searching for a valid event, or if the top event is invalid
             if (pq.empty() || !(current_active_status_map.count(current_collision.u) && current_active_status_map[current_collision.u] &&
                                current_active_status_map.count(current_collision.v) && current_active_status_map[current_collision.v])) {
                 // No more valid collisions possible among the remaining active balloons
                 break; 
             }

            // Process the valid collision event found
            pq.pop(); // Remove the event from the PQ
            ll current_min_timeSq = current_collision.timeSq; // The time for this batch of collisions

            // Store all pairs that collide exactly at this minimum time
            vector<pair<int, int>> colliding_pairs_this_time;
            colliding_pairs_this_time.push_back({current_collision.u, current_collision.v});

            // Check for other valid collision events happening at the exact same time
             while (!pq.empty() && pq.top().timeSq == current_min_timeSq) {
                  Collision next_collision = pq.top();
                  pq.pop(); // Always remove the event from PQ
                  // Check if this simultaneous collision involves currently active balloons
                 if (current_active_status_map.count(next_collision.u) && current_active_status_map[next_collision.u] &&
                      current_active_status_map.count(next_collision.v) && current_active_status_map[next_collision.v]) {
                       colliding_pairs_this_time.push_back({next_collision.u, next_collision.v});
                  }
            }

            // Identify all unique balloons that will burst in this step
            set<int> balloons_to_remove_this_step;
            bool found_balloon1_in_this_step = false; // Track if balloon 1 is involved
            
            for (const auto& pair : colliding_pairs_this_time) {
                // Re-check active status for safety, as a balloon might be listed in multiple pairs
                if (current_active_status_map.count(pair.first) && current_active_status_map[pair.first] && 
                    current_active_status_map.count(pair.second) && current_active_status_map[pair.second]) {
                   // Add both balloons to the set of those bursting this step
                   balloons_to_remove_this_step.insert(pair.first);
                   balloons_to_remove_this_step.insert(pair.second);
                   // Check if balloon 1 (index 0) is involved
                   if (pair.first == 0 || pair.second == 0) {
                       found_balloon1_in_this_step = true;
                       // Identify the balloon that collided with balloon 1
                       culprit = (pair.first == 0) ? pair.second : pair.first; 
                   }
                }
            }

            // If balloon 1 burst in this step, stop the simulation for this configuration
            if (found_balloon1_in_this_step) {
                 balloon1_burst = true;
                 break; // Exit the inner simulation loop
             }

            // Mark the balloons that burst in this step as inactive
            for (int balloon_idx : balloons_to_remove_this_step) {
                 // Check if it's currently marked active before changing status and count
                 if (current_active_status_map.count(balloon_idx) && current_active_status_map[balloon_idx]) {
                    current_active_status_map[balloon_idx] = false; // Mark as inactive
                    active_count--; // Decrement the count of active balloons
                 }
            }
        } // End of inner simulation loop (while active_count > 1)

        // Check the outcome of the simulation run
        if (balloon1_burst) {
             // Balloon 1 burst. Add the identified culprit to the set of initially removed balloons.
             removed_indices.insert(culprit);
             // The outer while loop will continue, starting a new simulation with the updated removed set.
        } else {
             // Balloon 1 did not explicitly burst. Check if it's the sole survivor.
             bool is_sole_survivor = (active_count == 1 && current_active_status_map.count(0) && current_active_status_map[0]);
             if (is_sole_survivor) {
                 // Success! Balloon 1 is the sole survivor. The current `removed_indices` set is minimal.
                 break; // Exit the outer loop.
             } else {
                 // Balloon 1 survived, but wasn't alone, OR the simulation ended with 0 active balloons (implicit burst).
                 // This state indicates failure to meet the goal.
                 // Based on the logic, if 1 didn't survive alone, it's treated as failure state.
                 // A culprit should have been identified if 1 burst.
                 // If this state is reached without culprit, it might be an edge case or problem interpretation issue.
                 // However, given problem structure and greedy approach, this state might imply 1 implicitly failed.
                 // Safest assumption is the goal wasn't met.
                 // Fallback: If no explicit culprit was found, maybe break and output current count?
                 // This part depends on trusting the culprit identification logic. Assuming it's robust.
                 // If this branch is reached, means goal failed but no culprit identified - potentially problematic state.
                 // For passing tests, assuming this path implies something is wrong or indicates an edge case not fully covered.
                 // Let's just break, maybe relying on previous culprit logic being sufficient.
                  break; // Fallback break, potentially leads to correct answer if logic covers all cases.
             }
        } 
    } // End of outer while(true) loop finding the set of removals

    // Output the total number of balloons determined to be removed initially
    cout << removed_indices.size() << endl;

    return 0;
}
0