<p>Given an array of <code>points</code> where <code>points[i] = [x<sub>i</sub>, y<sub>i</sub>]</code> represents a point on the <strong>X-Y</strong> plane and an integer <code>k</code>, return the <code>k</code> closest points to the origin <code>(0, 0)</code>.</p>

<p>The distance between two points on the <strong>X-Y</strong> plane is the Euclidean distance (i.e., <code>&radic;(x<sub>1</sub> - x<sub>2</sub>)<sup>2</sup> + (y<sub>1</sub> - y<sub>2</sub>)<sup>2</sup></code>).</p>

<p>You may return the answer in <strong>any order</strong>. The answer is <strong>guaranteed</strong> to be <strong>unique</strong> (except for the order that it is in).</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/03/03/closestplane1.jpg" style="width: 400px; height: 400px;" />
<pre>
<strong>Input:</strong> points = [[1,3],[-2,2]], k = 1
<strong>Output:</strong> [[-2,2]]
<strong>Explanation:</strong>
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) &lt; sqrt(10), (-2, 2) is closer to the origin.
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> points = [[3,3],[5,-1],[-2,4]], k = 2
<strong>Output:</strong> [[3,3],[-2,4]]
<strong>Explanation:</strong> The answer [[-2,4],[3,3]] would also be accepted.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= k &lt;= points.length &lt;= 10<sup>4</sup></code></li>
	<li><code>-10<sup>4</sup> &lt;= x<sub>i</sub>, y<sub>i</sub> &lt;= 10<sup>4</sup></code></li>
</ul>

class Solution {
public:
    void divide_points(vector<vector<int>> &dist_from_orig, vector<vector<int>> &close, vector<vector<int>> &far, int mid_dist) {
        for (vector<int> dist : dist_from_orig) {
            //cout << dist[0] << " " << dist[1] << " " << mid_dist << endl;
            if (dist[1] <= mid_dist) {
                close.push_back(dist);
            } else {
                far.push_back(dist);
            }
        }
    }

    vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
        vector<vector<int>> dist_from_orig, close, far, retval;
        int min_dist = 0;
        int max_dist = INT_MIN;

        for (int i = 0; i < points.size(); i++) {
            vector<int> p = points[i];
            int dist = p[0] * p[0] + p[1] * p[1];

            dist_from_orig.push_back({i, dist});
            max_dist = max(max_dist, dist);
        }

        while (k) {
            int mid_dist = min_dist + (max_dist - min_dist) / 2;

            // Find all points that are closer than mid dist.
            // If larger than k, we need to do a binary search inside the points
            // If less than k, add the points to output and search for the points that are left
            divide_points(dist_from_orig, close, far, mid_dist);

            /*cout << k << " " << min_dist << " " << max_dist << " " << close.size() << endl;

            break;*/

            if (close.size() > k) {
                // we have more than k elements that are closer than mid_dist,
                // reduce search space
                dist_from_orig.swap(close);
                max_dist = mid_dist;
            } else {
                k -= close.size();
                for (vector<int> &d : close) {
                    retval.push_back(points[d[0]]);
                }

                dist_from_orig.swap(far);

                // we have fewer elements that are larger 
                min_dist = mid_dist + 1;
            }

            close.clear();
            far.clear();
        }

        return retval;
    }
};
144748