A key aspect in approaching this problem is more humanly characterizing what makes a 4-connected subset valid. For each row with at least one cell in the subset, the subset's cells in this row must make up a contiguous range. This is necessary and sufficient to satisfy the third constraint in the problem statement.
However, we must characterize when the fourth constraint is also satisfied. First, the rows which have any subset cells must be a contiguous range of rows (otherwise it would not be 4-connected). Thus, let us characterize these rows. We can denote the column of the leftmost cell in the -th row (among those with cells in the subset) as and the column of the rightmost cell in the -th row as . To maintain 4-connectedness, each pair of consecutive rows must have some overlap (a necessary and sufficient requirement for this is that and ). Finally, must be non-increasing for some prefix and then non-decreasing for the remaining (likewise must be non-decreasing and then non-increasing). If this condition is not met, then there will be a violation of the fourth condition. If this condition is met, there will be no such violation. Thus, we finalize our characterization by saying this property must hold for and .
Now, we must calculate the number of subsets that satisfy this characterization and contain only grass. Our main intuition is that we would like to use dynamic programming, where our state is the current row we are looking at, the starting and ending column of a contiguous range within this row, and flags that indicate whether our should be non-increasing or non-decreasing (and likewise for ). There would be such states, and we could use this to ask how many valid subsets there are such that the bottom row of the subset is exactly this contiguous range of the row with these flags.
To be more concrete, one such method is supposing corresponds to the number of valid subsets with bottom row , cells from column to within , and flags and at the end of this process. The flag in corresponds to if is in the non-increasing stage and if it is in the non-decreasing stage. Likewise with the flag for , it is for non-decreasing and for non-increasing.
If we have computed the DP values for row , we can compute the values for row .
For example, (denoting row , leftmost column , rightmost column , is in the non-increasing prefix, and is in the non-decreasing prefix) is equal to if there is a non-grass cell between columns and at row , and otherwise equal to:
If we assume DP values are 0 when , then we can also use the following sum such that the sum is over a rectangle:
Note that there are more summands with different cases of flags. A naive transition given and is to loop over possible and , which would result in an running time solution which is sufficient for the first two subtasks.
To speed up this solution, we can observe that the logic of this transition can instead be replaced with operations using prefix sums with DP values.
We maintain the required values in time for each row, enabling a solution with runtime.
Danny Mittal's code:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class BeautifulSubsets { public static final long MOD = 1000000007; public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine()); boolean[][] grid = new boolean[n][n]; for (int y = 0; y < n; y++) { String line = in.readLine(); for (int x = 0; x < n; x++) { grid[y][x] = line.charAt(x) == 'G'; } } long answer = 0; long[][][][] dpPrev = new long[2][2][n][n]; for (int y = 0; y < n; y++) { long[][][][] sums1 = new long[2][2][n][n]; for (int x2 = 0; x2 < n; x2++) { for (int x1 = x2; x1 >= 0; x1--) { for (int b = 0; b < 2; b++) { sums1[0][b][x1][x2] = dpPrev[0][b][x1][x2]; if (x1 < x2) { sums1[0][b][x1][x2] += sums1[0][b][x1 + 1][x2]; sums1[0][b][x1][x2] %= MOD; } } } for (int x1 = 0; x1 <= x2; x1++) { for (int b = 0; b < 2; b++) { sums1[1][b][x1][x2] = dpPrev[1][b][x1][x2]; if (x1 > 0) { sums1[1][b][x1][x2] += sums1[1][b][x1 - 1][x2] + dpPrev[0][b][x1 - 1][x2]; sums1[1][b][x1][x2] %= MOD; } } } } long[][][][] sums2 = new long[2][2][n][n]; for (int x1 = 0; x1 < n; x1++) { for (int x2 = x1; x2 < n; x2++) { for (int a = 0; a < 2; a++) { sums2[a][0][x1][x2] = sums1[a][0][x1][x2]; if (x2 > x1) { sums2[a][0][x1][x2] += sums2[a][0][x1][x2 - 1]; sums2[a][0][x1][x2] %= MOD; } } } for (int x2 = n - 1; x2 >= x1; x2--) { for (int a = 0; a < 2; a++) { sums2[a][1][x1][x2] = sums1[a][1][x1][x2]; if (x2 < n - 1) { sums2[a][1][x1][x2] += sums2[a][1][x1][x2 + 1] + sums1[a][0][x1][x2 + 1]; sums2[a][1][x1][x2] %= MOD; } } } } long[][][][] dpNext = new long[2][2][n][n]; for (int x1 = 0; x1 < n; x1++) { boolean valid = true; for (int x2 = x1; x2 < n; x2++) { valid = valid && grid[y][x2]; if (valid) { dpNext[0][0][x1][x2] = (sums2[0][0][x1][x2] + 1L) % MOD; dpNext[0][1][x1][x2] = (sums2[0][1][x1][x2] - (x2 == n - 1 ? 0L : (sums2[0][1][x2 + 1][x2 + 1] + dpPrev[0][0][x2 + 1][x2 + 1])) + (2L * MOD)) % MOD; dpNext[1][0][x1][x2] = sums2[1][0][x1][x2]; dpNext[1][1][x1][x2] = sums2[1][1][x1][x2]; } else { for (int a = 0; a < 2; a++) { for (int b = 0; b < 2; b++) { dpNext[a][b][x1][x2] = 0; } } } for (int a = 0; a < 2; a++) { for (int b = 0; b < 2; b++) { answer += dpNext[a][b][x1][x2]; answer %= MOD; } } } } dpPrev = dpNext; } System.out.println(answer); } }