Hard

Maximum Partition FactorC++

Full explanation · Time O(n^2 * logn) · Space O(n^2)

// Time:  O(n^2 * logn)
// Space: O(n^2)

// greedy, sort, union find with parity
class Solution {
public:
    int maxPartitionFactor(vector<vector<int>>& points) {
        const auto& dist = [&](auto u, auto v) {
            return abs(points[u][0] - points[v][0]) + abs(points[u][1] - points[v][1]);
        };

        vector<tuple<int, int, int>> sorted_dists;
        for (int u = 0; u < size(points); ++u) {
            for (int v = u + 1; v < size(points); ++v) {
                sorted_dists.emplace_back(dist(u, v), u, v);
            }
        }
        sort(begin(sorted_dists), end(sorted_dists));
        UnionFind uf(size(points));
        for (const auto& [d, u, v] : sorted_dists) {
            if (!uf.union_set(u, v)) {
                return d;
            }
        }
        return 0;
    }

private:
    class UnionFind {
    public:
        UnionFind(int n)
            : set_(n)
            , rank_(n)
            , parity_(n) {
            iota(set_.begin(), set_.end(), 0);
        }

        int find_set(int x) {
            vector<int> stk;
            while (set_[x] != x) {  // path compression
                stk.emplace_back(x);
                x = set_[x];
            }
            while (!empty(stk)) {
                const int y = stk.back(); stk.pop_back();
                parity_[y] ^= parity_[set_[y]];  // added
                set_[y] = x;
            }
            return x;
        }

        bool union_set(int x, int y) {
            int ox = x, oy = y;
            x = find_set(x), y = find_set(y);
            if (x == y) {
                return parity_[ox] != parity_[oy]; // modified
            }
            if (rank_[x] > rank_[y]) {
                swap(x, y);
            }
            if (rank_[x] == rank_[y]) {
                ++rank_[y];
            }
            set_[x] = y;  // Union by rank.
            parity_[x] = parity_[ox] ^ parity_[oy] ^ 1;
            return true;
        }

    private:
        vector<int> set_;
        vector<int> rank_;
        vector<int> parity_;  // added
    };
};

// Time:  O(n^2 * logn)
// Space: O(n^2)
// greedy, sort, union find
class Solution2 {
public:
    int maxPartitionFactor(vector<vector<int>>& points) {
        const auto& dist = [&](auto u, auto v) {
            return abs(points[u][0] - points[v][0]) + abs(points[u][1] - points[v][1]);
        };

        vector<tuple<int, int, int>> sorted_dists;
        for (int u = 0; u < size(points); ++u) {
            for (int v = u + 1; v < size(points); ++v) {
                sorted_dists.emplace_back(dist(u, v), u, v);
            }
        }
        sort(begin(sorted_dists), end(sorted_dists));
        vector<int> lookup(size(points), -1);
        UnionFind uf(size(points));
        for (const auto& [d, u, v] : sorted_dists) {
            if (uf.find_set(u) == uf.find_set(v)) {
                return d;
            }
            if (lookup[u] != -1) {
                uf.union_set(lookup[u], v);
            } else {
                lookup[u] = v;
            }
            if (lookup[v] != -1) {
                uf.union_set(lookup[v], u);
            } else {
                lookup[v] = u;
            }
        }
        return 0;
    }

private:
    class UnionFind {
    public:
        UnionFind(int n)
            : set_(n)
            , rank_(n) {
            iota(set_.begin(), set_.end(), 0);
        }

        int find_set(int x) {
            vector<int> stk;
            while (set_[x] != x) {  // path compression
                stk.emplace_back(x);
                x = set_[x];
            }
            while (!empty(stk)) {
                const int y = stk.back(); stk.pop_back();
                set_[y] = x;
            }
            return x;
        }

        bool union_set(int x, int y) {
            x = find_set(x), y = find_set(y);
            if (x == y) {
                return false;
            }
            if (rank_[x] > rank_[y]) {
                swap(x, y);
            }
            if (rank_[x] == rank_[y]) {
                ++rank_[y];
            }
            set_[x] = y;  // Union by rank.
            return true;
        }

    private:
        vector<int> set_;
        vector<int> rank_;
    };
};

// Time:  O(n^2 * logn)
// Space: O(n^2)
// binary search, bfs, coordinate compression
class Solution3 {
public:
    int maxPartitionFactor(vector<vector<int>>& points) {
        static const int INF = numeric_limits<int>::max();

        const auto& binary_search_right = [](auto left, auto right, const auto& check) {
            while (left <= right) {
                const auto mid = left + (right - left) / 2;
                if (!check(mid)) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            return right;
        };

        const auto& dist = [&](auto u, auto v) {
            return abs(points[u][0] - points[v][0]) + abs(points[u][1] - points[v][1]);
        };

        const auto& is_bipartite = [&](auto d) {
            vector<int> lookup(size(points), -1);
            const auto& bfs = [&](auto u) {
                if (lookup[u] != -1) {
                    return true;
                }
                lookup[u] = 0;
                vector<int> q = {u};
                while (!empty(q)) {
                    vector<int> new_q;
                    for (const auto& u : q) {
                        for (int v = 0; v < size(points); ++v) {
                            if (!(v != u && dist(v, u) < d)) {
                                continue;
                            }
                            if (lookup[v] != -1) {
                                if (lookup[v] != lookup[u] ^ 1) {
                                    return false;
                                }
                                continue;
                            }
                            lookup[v] = lookup[u] ^ 1;
                            new_q.emplace_back(v);
                        }
                    }
                    q = move(new_q);
                }
                return true;
            };

            for (int u = 0; u < size(points); ++u) {
                if (!bfs(u)) {
                    return false;
                }
            }
            return true;
        };

        vector<int> sorted_dists;
        for (int u = 0; u < size(points); ++u) {
            for (int v = u + 1; v < size(points); ++v) {
                sorted_dists.emplace_back(dist(u, v));
            }
        }
        sorted_dists.emplace_back(INF);
        sort(begin(sorted_dists), end(sorted_dists));
        auto it = unique(begin(sorted_dists), end(sorted_dists));
        sorted_dists.erase(it, end(sorted_dists));
        int left = 0, right = size(sorted_dists) - 1;
        const auto& result = binary_search_right(left, right, [&](auto i) { return is_bipartite(sorted_dists[i]); });
        return sorted_dists[result] != INF ? sorted_dists[result] : 0;
    }
};

// Time:  O(n^2 * logr)
// Space: O(n)
// binary search, bfs
class Solution4 {
public:
    int maxPartitionFactor(vector<vector<int>>& points) {
        const auto& binary_search_right = [](auto left, auto right, const auto& check) {
            while (left <= right) {
                const auto mid = left + (right - left) / 2;
                if (!check(mid)) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            return right;
        };

        const auto& dist = [&](auto u, auto v) {
            return abs(points[u][0] - points[v][0]) + abs(points[u][1] - points[v][1]);
        };

        const auto& is_bipartite = [&](auto d) {
            vector<int> lookup(size(points), -1);
            const auto& bfs = [&](auto u) {
                if (lookup[u] != -1) {
                    return true;
                }
                lookup[u] = 0;
                vector<int> q = {u};
                while (!empty(q)) {
                    vector<int> new_q;
                    for (const auto& u : q) {
                        for (int v = 0; v < size(points); ++v) {
                            if (!(v != u && dist(v, u) < d)) {
                                continue;
                            }
                            if (lookup[v] != -1) {
                                if (lookup[v] != lookup[u] ^ 1) {
                                    return false;
                                }
                                continue;
                            }
                            lookup[v] = lookup[u] ^ 1;
                            new_q.emplace_back(v);
                        }
                    }
                    q = move(new_q);
                }
                return true;
            };

            for (int u = 0; u < size(points); ++u) {
                if (!bfs(u)) {
                    return false;
                }
            }
            return true;
        };

        int mx = 0;
        for (int u = 0; u < size(points); ++u) {
            for (int v = u + 1; v < size(points); ++v) {
                mx = max(mx, dist(u, v));
            }
        }
        int left = 0, right = mx + 1;
        const auto& result = binary_search_right(left, right, is_bipartite);
        return result != mx + 1 ? result : 0;
    }
};