import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
public static void main
(String[] args
) { FastReader sc = new FastReader();
if (n == null) return;
List<Point> points = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
points.
add(new Point(i, x, y
)); }
PairResult result = closestPair(points);
int index1
= Math.
min(result.
p1.
index, result.
p2.
index); int index2
= Math.
max(result.
p1.
index, result.
p2.
index);
System.
out.
printf("%d %d %.6f%n", index1, index2, result.
dist); }
/*
This is the pre-processing sorting step explained in the lab report.
This method uses .sort method and looking and reading the method from
IntelliJ (if you use this IDE), it explained that this method implemented
Tim Sort, making the time complexity O(n log n) in the worst and average case
*/
public static PairResult closestPair(List<Point> points) {
int n = points.size();
List<Point> pointsByX = new ArrayList<>(points);
List<Point> pointsByY = new ArrayList<>(points);
pointsByX.sort((p1, p2) -> {
if (p1.
x != p2.
x) return Double.
compare(p1.
x, p2.
x); if (p1.
y != p2.
y) return Double.
compare(p1.
y, p2.
y); return Integer.
compare(p1.
index, p2.
index); });
pointsByY.
sort(Comparator.
comparingDouble(p
-> p.
y));
return closestPairRes(pointsByX, pointsByY);
}
private static PairResult closestPairRes(List<Point> pointsByX, List<Point> pointsByY) {
int n = pointsByX.size();
/*
Simple base cases. If the length of points is only 2, return the two points and their distance.
If length of points is three, then do a brute force comparison.
*/
if (n == 2) {
return new PairResult(pointsByX.get(0), pointsByX.get(1), dist(pointsByX.get(0), pointsByX.get(1)));
}
if (n == 3) {
Point p1
= pointsByX.
get(0); Point p2
= pointsByX.
get(1); Point p3
= pointsByX.
get(2);
double dist1 = dist(p1, p2);
double dist2 = dist(p2, p3);
double dist3 = dist(p3, p1);
if (dist1 <= dist2 && dist1 <= dist3) {
return new PairResult(p1, p2, dist1);
} else if (dist2 <= dist1 && dist2 <= dist3) {
return new PairResult(p2, p3, dist2);
} else {
return new PairResult(p1, p3, dist3);
}
}
int mid = n / 2;
Point midPoint
= pointsByX.
get(mid
);
List<Point> leftX = pointsByX.subList(0, mid);
List<Point> rightX = pointsByX.subList(mid, n);
List<Point> leftY = new ArrayList<>(mid);
List<Point> rightY = new ArrayList<>(n - mid);
/*
Putting the Y-sorted points into left and right sub arrays.
By iterating through the already sorted pointsByY list, we guaranteed
that leftY and rightY remain perfectly sorted by Y-coordinates.
This avoid another O(n log n) resort inside the recursive step
making this O(n log^2 n) and instead keeping this step strictly O(n)
*/
for (Point p
: pointsByY
) { if (isLeftOf(p, midPoint)) {
leftY.add(p);
} else {
rightY.add(p);
}
}
/*
This is the recursive step where closestPairRes() calls itself, the code below splits into
two sub problems of size n/2.
*/
PairResult dl = closestPairRes(leftX, leftY);
PairResult dr = closestPairRes(rightX, rightY);
/*
delta (d) is the minimum distance strictly found on the left or the right.
Any pair of points spanning across the median line MUST have a distance
smaller than this d to be the true closest pair.
*/
PairResult closestPairResult = (dl.dist < dr.dist) ? dl : dr;
double d = closestPairResult.dist;
List<Point> strip = new ArrayList<>();
/*
Build the vertical strip centered at the median X-coordinate.
We only include points that are within a horizontal distance of d.
*/
for (Point p
: pointsByY
) { if (Math.
abs(p.
x - midPoint.
x) <= d
) { strip.add(p);
}
}
/*
This strip is sorted by Y, the condition (strip.get(j).y - strip.get(i).y) < d
created a strict 2d x d bounding box above the candidate point. By the Pigeonhole
Principle, this region can hold a maximum of 8 points. That's why mathematically
the loop is guaranteed to fail the distance Y check and break after at most 7 comparison
keeping this step still O(n)
*/
for (int i = 0; i < strip.size(); i++) {
for (int j = i + 1; j < strip.size() && (strip.get(j).y - strip.get(i).y) < d; j++) {
double dist = dist(strip.get(i), strip.get(j));
if (dist < d) {
d = dist;
closestPairResult = new PairResult(strip.get(i), strip.get(j), dist);
}
}
}
return closestPairResult;
}
// Helper Methods / Classes
public static boolean isLeftOf
(Point p,
Point midpoint
) { if (p.x != midpoint.x) return p.x < midpoint.x;
if (p.y != midpoint.y) return p.y < midpoint.y;
return p.index < midpoint.index;
}
double dx = a.x - b.x;
double dy = a.y - b.y;
return Math.
sqrt((dx
* dx
) + (dy
* dy
)); }
private static class PairResult {
private final double dist;
public PairResult
(Point p1,
Point p2,
double dist
) { this.p1 = p1;
this.p2 = p2;
this.dist = dist;
}
}
private static class Point { private final int index;
private double x, y;
public Point(int index,
double x,
double y
) { this.index = index;
this.x = x;
this.y = y;
}
}
// GeeksforGeeks FastReader Implementation
// Using this to hopefully get a accepted answer in SPOJ
// Since it's a competitive website and needs a efficient I/O
static class FastReader {
public FastReader() {
}
while (st == null || !st.hasMoreElements()) {
try {
if (line == null || line.trim().isEmpty()) {
return null; // Handle EOF safely
}
e.printStackTrace();
return null;
}
}
return st.nextToken();
}
if (str == null) return null;
}
}
}