Hard

Minimum Time For K Virus Variants to SpreadC++

Full explanation · Time O(nlogn * logr) · Space O(n)

// Time:  O(nlogn * logr), r is the sum of range x size and range y size
// Space: O(n)

// competitive programming solution
class Solution {
public:
    int minDayskVariants(vector<vector<int>>& points, int k) {
        std::transform(begin(points), end(points), begin(points),
                       [](const auto& v) {
                           return vector<int>({v[0] + v[1], v[0] - v[1]});  // rotate
                       });
        const auto& compare_y = [](const vector<int>& a, const vector<int>& b) { return a[1] < b[1]; };
        const int min_x = (*min_element(cbegin(points), cend(points)))[0];
        const int max_x = (*max_element(cbegin(points), cend(points)))[0];
        const int min_y = (*min_element(cbegin(points), cend(points), compare_y))[1];
        const int max_y = (*max_element(cbegin(points), cend(points), compare_y))[1];
        int64_t left = 0, right = ((int64_t(max_x) - min_x) + (int64_t(max_y) - min_y) + 1) / 2;
        while (left <= right) {
            const auto& mid = left + (right - left) / 2;
            if (check(points, k, mid)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;                       
    }

private:
    class SegmentTree {
    public:
        SegmentTree(int N)
            : N_(N),
              tree_(2 * N),
              lazy_(N)
        {
            H_ = 1;
            while ((1 << H_) < N) {
                ++H_;
            }
        }

        void update(int L, int R, int h) {
            L += N_; R += N_;
            int L0 = L, R0 = R;
            while (L <= R) {
                if ((L & 1) == 1) {
                    apply(L++, h);
                }
                if ((R & 1) == 0) {
                    apply(R--, h);
                }
                L >>= 1; R >>= 1;
            }
            pull(L0); pull(R0);
        }

        int query(int L, int R) {
            L += N_; R += N_;
            auto result = 0;
            push(L); push(R);
            while (L <= R) {
                if ((L & 1) == 1) {
                    result = max(result, tree_[L++]);
                }
                if ((R & 1) == 0) {
                    result = max(result, tree_[R--]);
                }
                L >>= 1; R >>= 1;
            }
            return result;
        }

    private:
        int N_, H_;
        vector<int> tree_, lazy_;

        void apply(int x, int val) {
            tree_[x] += val;
            if (x < N_) {
                lazy_[x] += val;
            }
        }

        void pull(int x) {
            while (x > 1) {
                x >>= 1;
                tree_[x] = max(tree_[x * 2], tree_[x * 2 + 1]);
                if (lazy_[x]) {
                    tree_[x] += lazy_[x];
                }
            }
        }

        void push(int x) {
            for (int h = H_; h > 0; --h) {
                int y = x >> h;
                if (lazy_[y] != 0) {
                    apply(y * 2, lazy_[y]);
                    apply(y * 2 + 1, lazy_[y]);
                    lazy_[y] = 0;
                }
            }
        }
    };

    bool check(const vector<vector<int>>& points, int k, int l) {  // Time: O(nlogn), Space: O(n)
        using Event = tuple<int64_t, int64_t, int64_t, int64_t>;
        vector<Event> intervals;
        unordered_set<int64_t> y_set;
        for (const auto& p : points) {
            // add [y0, y1] by 1 in [x0, x1+1)
            int64_t x0 = int64_t(p[0]) - l, y0 = int64_t(p[1]) - l, x1 = int64_t(p[0]) + l, y1 = int64_t(p[1]) + l;
            intervals.emplace_back(x0    , +1, y0, y1);
            intervals.emplace_back(x1 + 1, -1, y0, y1);
            y_set.emplace(y0);
            y_set.emplace(y1);
        }
        sort(begin(intervals), end(intervals));
        vector<int64_t> sorted_y(cbegin(y_set), cend(y_set));
        sort(begin(sorted_y), end(sorted_y));
        unordered_map<int64_t, int> y_to_idx;
        for (int i = 0; i < size(sorted_y); ++i) {  // coordinate compression
            y_to_idx[sorted_y[i]] = i;
        }
        SegmentTree st(size(y_to_idx));
        for (const auto& [_, v, y0, y1] : intervals) {  // line sweep
            st.update(y_to_idx[y0], y_to_idx[y1], v);
            if (st.query(0, size(y_to_idx) - 1) >= k) {
                return true;
            }
        }
        return false;
    }
};

// Time:  O(n^2 * logr), r is the sum of range x size and range y size
// Space: O(n)
// interview solution
class Solution2 {
public:
    int minDayskVariants(vector<vector<int>>& points, int k) {
        std::transform(begin(points), end(points), begin(points),
                       [](const auto& v) {
                           return vector<int>({v[0] + v[1], v[0] - v[1]});  // rotate
                       });
        const auto& compare_y = [](const vector<int>& a, const vector<int>& b) { return a[1] < b[1]; };
        const int min_x = (*min_element(cbegin(points), cend(points)))[0];
        const int max_x = (*max_element(cbegin(points), cend(points)))[0];
        const int min_y = (*min_element(cbegin(points), cend(points), compare_y))[1];
        const int max_y = (*max_element(cbegin(points), cend(points), compare_y))[1];
        int64_t left = 0, right = ((int64_t(max_x) - min_x) + (int64_t(max_y) - min_y) + 1) / 2;
        while (left <= right) {
            const auto& mid = left + (right - left) / 2;
            if (check(points, k, mid)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;                       
    }

private:
    bool check(const vector<vector<int>>& points, int k, int l) {  // Time: O(n^2), Space: O(n)
        unordered_map<int64_t, unordered_map<int64_t, int>> intervals;
        unordered_set<int64_t> y_set;
        for (const auto& p : points) {
            // add [y0, y1+1) by 1 in [x0, x1+1)
            int64_t x0 = int64_t(p[0]) - l, y0 = int64_t(p[1]) - l, x1 = int64_t(p[0]) + l, y1 = int64_t(p[1]) + l;
            ++intervals[x0][y0];
            --intervals[x0][y1 + 1];
            --intervals[x1 + 1][y0];
            ++intervals[x1 + 1][y1 + 1];
            y_set.emplace(y0);
            y_set.emplace(y1 + 1);
        }
        vector<int64_t> sorted_x;
        for (const auto& [x, _] : intervals) {
            sorted_x.emplace_back(x);
        }
        sort(begin(sorted_x), end(sorted_x));
        vector<int64_t> sorted_y(cbegin(y_set), cend(y_set));
        sort(begin(sorted_y), end(sorted_y));
        unordered_map<int64_t, int> count;
        for (const auto& x : sorted_x) {  // line sweep
            for (const auto& [y, c] : intervals[x]) {
                count[y] += c;
            }
            int cnt = 0;
            for (const auto& y : sorted_y) {
                cnt += count[y];
                if (cnt >= k) {
                    return true;
                }
            }
        }
        return false;
    }
};