The Problem, in Plain Words
You're given a grid of 0s and 1s, and a number k. You want to slice the grid into exactly k rectangular pieces, where every piece must contain at least one 1.
The catch is in how you're allowed to cut:
A cut is either horizontal (splits into top/bottom) or vertical (splits into left/right).
After a horizontal cut, only the bottom piece can be cut again.
After a vertical cut, only the right piece can be cut again.
In other words, once you cut off a piece, that piece is "final"—it's locked in as one of your k pieces, and it never gets touched again. Only the remaining rectangle (bottom or right) stays "in play" for further cutting.
We need to count how many distinct sequences of cuts produce exactly k valid pieces, modulo 1e9 + 7.
The Key Observation
At first glance, this looks like it could require tracking arbitrary sub-rectangles of the matrix—which would blow up the state space. But look closely at the cutting rule again:
Horizontal cut → only the bottom survives.
Vertical cut → only the right survives.
This means the "still cuttable" region is never an arbitrary rectangle. It is always a suffix rectangle—defined by a starting row r and starting column c, extending all the way to the bottom-right corner of the original matrix: rows [r, n-1] and columns [c, m-1].
Why? Because:
Cutting horizontally at row
ishrinks the top boundary fromrtoi, but the bottom is stilln-1and columns are untouched.Cutting vertically at column
jshrinks the left boundary fromctoj, but the right is stillm-1and rows are untouched.
So no matter how many cuts you chain together, the "remaining piece" is always described by just two numbers: (r, c). That's the whole trick—it turns an exponential-looking problem into something with only O(n * m) possible active regions.
Defining the DP State
We define:
dp[r][c][p] = number of ways to split the suffix-rectangle
rows [r, n-1], cols [c, m-1]
into exactly p valid pieces (each containing ≥1 one)
Our final answer is:
dp[0][0][k]
which represents splitting the whole matrix into k pieces.
Base Case: p = 1
If we only need one piece, there's nothing left to cut. It's valid if and only if the rectangle contains at least one 1.
dp[r][c][1] = 1 if sum(rows[r..n-1], cols[c..m-1]) > 0
dp[r][c][1] = 0 otherwise
Recursive Case: p > 1
To split the rectangle (r, c) into p pieces, we make one cut—either horizontal or vertical—that peels off a valid piece, and recurse on the rest with p - 1 pieces remaining.
Horizontal Cut
For a cut at row i (r < i <= n-1):
The piece cut off is rows
[r, i-1], columns[c, m-1]. This must contain at least one1.The remaining rectangle, rows
[i, n-1], columns[c, m-1], must be split intop-1pieces:
dp[i][c][p-1]
Vertical Cut
For a cut at column j (c < j <= m-1):
The piece cut off is rows
[r, n-1], columns[c, j-1]. This must contain at least one1.The remaining rectangle is:
dp[r][j][p-1]
So:
dp[r][c][p] = Σ dp[i][c][p-1] over all valid horizontal cut rows i
+ Σ dp[r][j][p-1] over all valid vertical cut columns j
If done naively, this sum runs over up to n + m cut positions for every one of the n * m * k states—giving O(n * m * k * (n + m)), which is too slow for the problem's constraints.
Speeding It Up: Monotonicity + Binary Search + Suffix Sums
Two tricks bring this down to the required O(n * m * k * log(n + m)).
Trick 1 — Monotonicity Lets Us Binary Search the Cut Boundary
As the cut row i moves further away from r, the peeled-off piece only grows, so the count of 1s in it is monotonically non-decreasing.
That means:
There is a smallest valid cut index.
Every cut after that index is also valid.
We binary search for this first valid index using the matrix's 2D prefix-sum array (rectSum) to check whether a piece contains at least one 1 in O(1) time.
This costs:
O(log n)for horizontal cuts.O(log m)for vertical cuts.
Trick 2 — Suffix Sums Replace Repeated DP Summations
Once we know the smallest valid cut index res, we need:
Σ dp[i][c][p-1] for i = res .. n-1
and
Σ dp[r][j][p-1] for j = res .. m-1
Instead of summing these ranges every time, we precompute suffix sum arrays once per DP layer.
rowSuffix[c][r]= sum ofdp[i][c][p-1]fori = r ... n-1colSuffix[r][c]= sum ofdp[r][j][p-1]forj = c ... m-1
These are built in O(n * m) per layer, and each required range sum becomes a single lookup.
Putting It Together
For each layer p from 1 to k:

Join the conversation! Your thoughts help the community grow.