/*
QUESTION 3: Path Break Probability in a Directed Grid
Problem Statement:
You are given a grid of open and blocked cells.
'.' means open.
'#' means blocked.
You start from the top-left cell and want to reach the bottom-right cell.
You may only move right or down.
Exactly one open cell is chosen uniformly at random and blocked.
Find the probability that after blocking this cell, no valid path remains.
Return the probability modulo 1e9 + 7.
Hard Constraints:
1 <= N, M
N * M <= 200000
Example:
grid =
[
"..",
".."
]
Expected Output:
500000004
Explanation:
There are 4 open cells.
If start or destination is blocked, no path remains.
If either of the other two cells is blocked, one path still remains.
Probability = 2 / 4 = 1 / 2
Modulo 1e9 + 7, this is 500000004.
Brute Force:
For every open cell:
- Temporarily block it.
- Run reachability DP.
- Count how many choices break all paths.
Time Complexity:
O(F * N * M)
Optimized Approach:
Compute:
fromStart[i][j] = whether cell can be reached from start.
toEnd[i][j] = whether destination can be reached from this cell.
A useful cell lies on some valid path if both are true.
Every right/down path crosses exactly one cell on every diagonal i + j.
If a diagonal contains only one useful cell, every path must pass through it.
Time Complexity:
O(N * M)
Space Complexity:
O(N * M)
*/
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
static const long long MOD = 1000000007;
long long modpow(long long a, long long e) {
long long res = 1;
while (e > 0) {
if (e & 1) res = res * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return res;
}
int pathFailureProbability(vector<string>& grid) {
int n = grid.size();
int m = grid[0].size();
int freeCells = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '.') freeCells++;
}
}
vector<vector<int>> fromStart(n, vector<int>(m, 0));
vector<vector<int>> toEnd(n, vector<int>(m, 0));
if (grid[0][0] == '.') {
fromStart[0][0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '#') continue;
if (i > 0 && fromStart[i - 1][j]) fromStart[i][j] = 1;
if (j > 0 && fromStart[i][j - 1]) fromStart[i][j] = 1;
}
}
}
if (grid[n - 1][m - 1] == '.') {
toEnd[n - 1][m - 1] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
if (grid[i][j] == '#') continue;
if (i + 1 < n && toEnd[i + 1][j]) toEnd[i][j] = 1;
if (j + 1 < m && toEnd[i][j + 1]) toEnd[i][j] = 1;
}
}
}
if (!fromStart[n - 1][m - 1]) return 1;
vector<int> diagonalUsefulCount(n + m - 1, 0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '.' && fromStart[i][j] && toEnd[i][j]) {
diagonalUsefulCount[i + j]++;
}
}
}
long long critical = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '.' && fromStart[i][j] && toEnd[i][j]) {
if (diagonalUsefulCount[i + j] == 1) {
critical++;
}
}
}
}
return critical * modpow(freeCells, MOD - 2) % MOD;
}
};
int main() {
Solution sol;
vector<string> grid = {
"..",
".."
};
cout << sol.pathFailureProbability(grid) << endl;
return 0;
}