結果
| 問題 |
No.2436 Min Diff Distance
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:06:27 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 273 ms / 2,000 ms |
| コード長 | 9,332 bytes |
| コンパイル時間 | 1,205 ms |
| コンパイル使用メモリ | 107,124 KB |
| 実行使用メモリ | 31,420 KB |
| 最終ジャッジ日時 | 2025-05-14 13:07:41 |
| 合計ジャッジ時間 | 6,296 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 23 |
ソースコード
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <limits>
#include <numeric> // Required for std::iota
// Define long long for large coordinates and distances
using ll = long long;
// Structure to represent a point in original (X, Y) and transformed (U, V) coordinates.
// U = X + Y, V = X - Y. Manhattan distance in (X, Y) is Chebyshev distance in (U, V).
// Also store its original index for identification.
struct Point {
ll x, y, u, v;
int id; // Original index 0 to N-1
};
// Structure for K-D tree node
struct KDNode {
Point point; // Stores the actual point data for the node
KDNode *left = nullptr;
KDNode *right = nullptr;
int axis; // Splitting axis: 0 for U, 1 for V
};
// Global vector to store all points. Accessed by buildKDTreeIdx using indices.
std::vector<Point> global_points;
// Comparison functors used with std::nth_element to partition indices based on point coordinates.
struct CompareUIdx {
bool operator()(int a, int b) const {
return global_points[a].u < global_points[b].u;
}
};
struct CompareVIdx {
bool operator()(int a, int b) const {
return global_points[a].v < global_points[b].v;
}
};
// Function to build the K-D tree recursively using indices into the global_points vector.
// This approach avoids copying large Point structures and uses less memory.
// The tree nodes still store Point data for efficient access during queries.
KDNode* buildKDTreeIdx(std::vector<int>& indices, int depth) {
// Base case: If no indices, return nullptr.
if (indices.empty()) {
return nullptr;
}
// Determine the splitting axis based on depth (alternating U and V).
int axis = depth % 2;
// Find the median index within the current list of indices.
size_t median_idx_in_indices = indices.size() / 2;
// Partition the indices using nth_element. This puts the index of the median point
// at the `median_idx_in_indices` position and ensures elements before are <= and after are >=
// based on the coordinate value along the current axis. This runs in O(N) average time.
if (axis == 0) { // Split on U axis
std::nth_element(indices.begin(), indices.begin() + median_idx_in_indices, indices.end(), CompareUIdx());
} else { // Split on V axis
std::nth_element(indices.begin(), indices.begin() + median_idx_in_indices, indices.end(), CompareVIdx());
}
// Get the index of the median point in the global_points vector.
int median_point_idx = indices[median_idx_in_indices];
// Create a new K-D tree node.
KDNode* node = new KDNode();
// The node stores the actual Point data corresponding to the median index.
node->point = global_points[median_point_idx];
node->axis = axis;
// Create index vectors for left and right children.
// Indices before the median go to the left child, indices after go to the right child.
// The median itself is stored in the current node.
std::vector<int> left_indices(indices.begin(), indices.begin() + median_idx_in_indices);
std::vector<int> right_indices(indices.begin() + median_idx_in_indices + 1, indices.end());
// Recursively build the left and right subtrees.
// Pass the newly created (copied) index vectors to the recursive calls.
node->left = buildKDTreeIdx(left_indices, depth + 1);
node->right = buildKDTreeIdx(right_indices, depth + 1);
return node; // Return the newly created node.
}
// Function to calculate Chebyshev distance (L_infinity norm) between two points using their (U, V) coordinates.
ll chebyshevDist(const Point& p1, const Point& p2) {
return std::max(std::abs(p1.u - p2.u), std::abs(p1.v - p2.v));
}
// Global variable to store the minimum distance found during a nearest neighbor query.
ll current_best_dist;
// Recursive function to query the K-D tree for the nearest neighbor of `query_point`.
// Updates `current_best_dist` globally.
void findNearest(KDNode* node, const Point& query_point) {
// Base case: If node is null, stop recursion.
if (node == nullptr) {
return;
}
// Calculate distance from the point stored in the current node to the query point.
// Important: Only consider this point if it's not the query point itself (check by ID).
if (node->point.id != query_point.id) {
ll dist = chebyshevDist(node->point, query_point);
// Update the global minimum distance if this point is closer.
current_best_dist = std::min(current_best_dist, dist);
}
// Determine which child subtree to visit first.
// Go towards the half-space containing the query point first.
KDNode *target_subtree, *other_subtree;
// Get the coordinate of the query point and node point along the node's splitting axis.
ll query_coord = (node->axis == 0) ? query_point.u : query_point.v;
ll node_coord = (node->axis == 0) ? node->point.u : node->point.v;
// Decide traversal order based on query point's position relative to the splitting plane.
if (query_coord < node_coord) {
target_subtree = node->left;
other_subtree = node->right;
} else {
target_subtree = node->right;
other_subtree = node->left;
}
// Recursively search the target subtree first.
findNearest(target_subtree, query_point);
// Pruning step: Check if the other subtree could possibly contain a closer point.
// Calculate the distance from the query point to the splitting plane.
ll axis_dist = std::abs(query_coord - node_coord);
// If the distance along the splitting axis is already greater than or equal to the current best distance,
// then no point in the other subtree can be closer (since Chebyshev distance is max of axis distances).
// So, only search the other subtree if `axis_dist < current_best_dist`.
if (axis_dist < current_best_dist) {
findNearest(other_subtree, query_point);
}
}
// Function to recursively delete the K-D tree nodes and free memory.
void deleteKDTree(KDNode* node) {
if (node == nullptr) return;
deleteKDTree(node->left);
deleteKDTree(node->right);
delete node;
}
int main() {
// Faster I/O operations
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int N; // Number of points
std::cin >> N;
// Problem constraint guarantees N >= 2.
// Resize global points vector once to avoid reallocations.
global_points.resize(N);
// Variables to track min/max U and V coordinates for calculating max distances.
ll min_U = std::numeric_limits<ll>::max();
ll max_U = std::numeric_limits<ll>::min();
ll min_V = std::numeric_limits<ll>::max();
ll max_V = std::numeric_limits<ll>::min();
// Read input points and compute transformed coordinates (U, V).
// Also find the min/max U and V values.
for (int i = 0; i < N; ++i) {
ll x, y;
std::cin >> x >> y;
global_points[i] = {x, y, x + y, x - y, i}; // Store point data with original index i
min_U = std::min(min_U, global_points[i].u);
max_U = std::max(max_U, global_points[i].u);
min_V = std::min(min_V, global_points[i].v);
max_V = std::max(max_V, global_points[i].v);
}
// Calculate maximum Manhattan distance M_i for each point i.
// In (U, V) coordinates, this is the maximum Chebyshev distance to any other point.
// This max distance is determined by the point's distance to the bounding box corners defined by min/max U and V.
std::vector<ll> M(N);
for (int i = 0; i < N; ++i) {
M[i] = std::max({global_points[i].u - min_U, max_U - global_points[i].u, global_points[i].v - min_V, max_V - global_points[i].v});
}
// Prepare initial vector of indices [0, 1, ..., N-1].
std::vector<int> initial_indices(N);
std::iota(initial_indices.begin(), initial_indices.end(), 0);
// Build the K-D Tree using the indices. Average time complexity O(N log N).
KDNode* root = buildKDTreeIdx(initial_indices, 0);
// Calculate minimum Manhattan distance m_i for each point i.
// This is equivalent to finding the nearest neighbor in (U, V) coordinates using Chebyshev distance.
std::vector<ll> m(N);
for (int i = 0; i < N; ++i) {
// Reset the global best distance for each query.
current_best_dist = std::numeric_limits<ll>::max();
// Perform nearest neighbor search. Average time complexity O(log N).
findNearest(root, global_points[i]);
// Store the found minimum distance for point i.
// Since N >= 2 and points are distinct, a nearest neighbor other than itself must exist.
// `current_best_dist` will be updated unless something is fundamentally wrong.
m[i] = current_best_dist;
}
// Calculate P_i = |M_i - m_i| for each point and find the minimum P_i.
// Since M_i is max distance and m_i is min distance, M_i >= m_i, so absolute value isn't strictly needed.
ll min_P = std::numeric_limits<ll>::max();
for (int i = 0; i < N; ++i) {
min_P = std::min(min_P, M[i] - m[i]);
}
// Output the final minimum P value.
std::cout << min_P << std::endl;
// Cleanup allocated memory for the K-D tree.
deleteKDTree(root);
return 0;
}
qwewe