Nasser's Grid Components#13101

Nasser's Grid Components (Editorial)

Problem Author:

Mohammad Nasser (MOAGEZA)

Prepared by:

Mohammad Nasser (MOAGEZA)

Edited by:

Hasan Saleh (MathModel)

Solution

General Idea:

Let L_c be the size of the largest connected component using only columns 1, \ldots, c, and let R_c be the size of the largest connected component using only columns c, \ldots, m. A split at column gives exactly L = L_c and R = R_{c+1}, so the answer is

\min_{1 \le c < m} \left| L_c - R_{c+1} \right|.

Computing L_c for all :

Adding a new column to the left part never splits a component; it can only create new cells, or merge existing components together. So we can use a Disjoint Set Union (DSU) that stores the size of each component, and add the columns one by one from left to right:

  • For each cell in column , if a_{i,j} = a_{i-1,j}, merge the two cells (vertical edge).

  • If a_{i,j} = a_{i,j-1}, merge the two cells (horizontal edge to the previous column).

  • Maintain , the maximum component size seen so far. Since component sizes never decrease, after finishing column we have L_j = \mathit{mx}.

Computing R_c for all :

Do exactly the same thing in the opposite direction: use a fresh DSU, add the columns from down to , merging each cell with the cell below it and with the cell to its right. After finishing column , set R_j = \mathit{mx}.

Combining:

Iterate over all 1 \le c < m and take the minimum of |L_c - R_{c+1}|.

Complexity:

Each of the n \cdot m cells performs a constant number of DSU operations in each of the two passes. With path compression, the solution runs in \mathcal{O}\!\left(\sum n \cdot m \cdot \alpha(n \cdot m)\right) time, which is practically linear, and uses \mathcal{O}(n \cdot m) memory.

#include <bits/stdc++.h>
using namespace std;

struct DSU {
    vector<int> p;
    DSU(int n) : p(n) { iota(p.begin(), p.end(), 0); }
    int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
    void unite(int a, int b) { p[find(a)] = find(b); }
};

void solve() {
    int n, m;
    cin >> n >> m;
    int sz = n * m + 5;
    vector<vector<int>> a(n + 2, vector<int>(m + 2, 0)), pos(n + 2, vector<int>(m + 2, 0));
    vector<int> res(sz, 0), L(m + 2, 0), R(m + 2, 0);

    int p = 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            pos[i][j] = p;
            res[p] = 1;
            p++;
        }

    auto join = [&](DSU &d, int u, int v, int &mx) {
        int x = d.find(u), y = d.find(v);
        if (x == y) return;
        d.unite(x, y);
        int z = d.find(x);
        res[z] = res[x] + res[y];
        mx = max(mx, res[z]);
    };

    // left to right
    DSU l(sz);
    int mx = 1;
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (a[i][j] == a[i - 1][j]) join(l, pos[i][j], pos[i - 1][j], mx);
            if (a[i][j] == a[i][j - 1]) join(l, pos[i][j], pos[i][j - 1], mx);
        }
        L[j] = mx;
    }

    // right to left
    for (int p2 = 1; p2 < sz; p2++) res[p2] = 1;
    DSU r(sz);
    mx = 1;
    for (int j = m; j >= 1; j--) {
        for (int i = 1; i <= n; i++) {
            if (i + 1 <= n && a[i][j] == a[i + 1][j]) join(r, pos[i][j], pos[i + 1][j], mx);
            if (j + 1 <= m && a[i][j] == a[i][j + 1]) join(r, pos[i][j], pos[i][j + 1], mx);
        }
        R[j] = mx;
    }

    int ans = 1e9;
    for (int c = 1; c < m; c++)
        ans = min(ans, abs(L[c] - R[c + 1]));
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}