結果
問題 |
No.1265 Balloon Survival
|
ユーザー |
![]() |
提出日時 | 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 |
ソースコード
#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; }