1. Abridged Problem Statement
Given K black cells on an infinite grid (all other cells are white), you repeatedly “complete” any axis-aligned rectangle whenever three of its corners are black and the fourth is white—turning that fourth corner black. When no more moves are possible, how many black cells remain?

2. Detailed Editorial
**Key Observation.** Once you can fill in a rectangle corner, you’ll eventually fill all “missing” cells in any connected block of rows and columns that are linked by the initial black cells. Formally:
- Think of each black cell as a point (x, y).
- Build a graph whose vertices are these points; connect two points if they share the same x (same row) or the same y (same column).
- In each connected component of this graph, let R be the number of *distinct* x-coordinates and C be the number of *distinct* y-coordinates present.
- You can show that the rectangle-filling process will eventually paint every cell in the Cartesian product R×C—i.e. R·C black cells in that component.

Hence the final answer is the sum over all components of (number of distinct rows) × (number of distinct columns).

Implementation Steps:
1. Read the K points into arrays x[i], y[i].
2. While reading, maintain two maps: one from x-coordinate to the index of the *last* point seen with that x, and similarly for y. Whenever you read a new point i, if you’ve seen that x before at index j, add an undirected edge i–j; likewise for y. This chains all points in a given row (or column) into a single connected component.
3. Run a flood fill (iterative DFS) over the graph’s K vertices. For each component, collect its distinct x’s and y’s in two sets; when the traversal finishes, add |rows|×|cols| to your answer.
4. Print the accumulated sum.

Time Complexity: O(K log K) from the maps (or O(K) if you precompress coordinates), plus O(K) for the graph traversal.

3. C++ Solution
```cpp
#include <bits/stdc++.h>

using namespace std;

template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) {
        in >> x;
    }
    return in;
};

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {
        out << x << ' ';
    }
    return out;
};

int n;
vector<int> x, y;
vector<vector<int>> adj;

void read() {
    cin >> n;
    x.resize(n);
    y.resize(n);
    adj.assign(n, {});

    map<int, int> col_last, row_last;
    for(int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];

        if(col_last.count(y[i])) {
            adj[i].push_back(col_last[y[i]]);
            adj[col_last[y[i]]].push_back(i);
        } else {
            col_last[y[i]] = i;
        }

        if(row_last.count(x[i])) {
            adj[i].push_back(row_last[x[i]]);
            adj[row_last[x[i]]].push_back(i);
        } else {
            row_last[x[i]] = i;
        }
    }
}

void solve() {
    // Each black cell links the previous cell sharing its row and the previous
    // cell sharing its column, building a graph whose connected components are
    // closed under the rectangle-completion move. Within one component every
    // (row, column) pair that appears can eventually be filled, so the final
    // count of black cells in that component is exactly the number of distinct
    // rows times the number of distinct columns it touches.
    //
    // Flood fill each component, collect its distinct row and column
    // coordinates in sets, and accumulate rows * cols across all components.

    vector<char> used(n, 0);
    int64_t answer = 0;

    for(int s = 0; s < n; s++) {
        if(used[s]) {
            continue;
        }

        set<int> rw, cl;
        vector<int> stack = {s};
        used[s] = 1;
        while(!stack.empty()) {
            int u = stack.back();
            stack.pop_back();
            rw.insert(x[u]);
            cl.insert(y[u]);
            for(int v: adj[u]) {
                if(!used[v]) {
                    used[v] = 1;
                    stack.push_back(v);
                }
            }
        }

        answer += (int64_t)rw.size() * (int64_t)cl.size();
    }

    cout << answer << '\n';
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    // cin >> T;
    for(int test = 1; test <= T; test++) {
        read();
        // cout << "Case #" << test << ": ";
        solve();
    }

    return 0;
}
```

4. Python Solution with Detailed Comments
```python
import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.readline

def main():
    K = int(input())
    xs = [0]*K
    ys = [0]*K
    # Map each x (resp. y) to the list of point indices having that coordinate
    map_x = {}
    map_y = {}

    for i in range(K):
        xi, yi = map(int, input().split())
        xs[i] = xi
        ys[i] = yi
        map_x.setdefault(xi, []).append(i)
        map_y.setdefault(yi, []).append(i)

    visited = [False]*K
    answer = 0

    # BFS/DFS will consume each group once; after exploring all points
    # on some x or y, we clear its list to avoid revisiting.
    from collections import deque
    for i in range(K):
        if not visited[i]:
            queue = deque([i])
            visited[i] = True
            comp_rows = set()
            comp_cols = set()

            while queue:
                u = queue.popleft()
                rx, ry = xs[u], ys[u]
                comp_rows.add(rx)
                comp_cols.add(ry)

                # Explore all other points sharing the same x
                for v in map_x.get(rx, []):
                    if not visited[v]:
                        visited[v] = True
                        queue.append(v)
                # Clear so we don't re-explore this row later
                map_x[rx] = []

                # Explore all other points sharing the same y
                for v in map_y.get(ry, []):
                    if not visited[v]:
                        visited[v] = True
                        queue.append(v)
                # Clear so we don't re-explore this column later
                map_y[ry] = []

            # This component can be filled to (#rows)×(#cols)
            answer += len(comp_rows) * len(comp_cols)

    print(answer)

if __name__ == "__main__":
    main()
```

5. Compressed Editorial
Model the black cells as nodes in a graph, linking any two on the same row or column. In each connected component, if there are R distinct rows and C distinct columns, the rectangle-completion process fills all R × C positions. Sum R·C over components.
