One Shared Corridor#13119

One Shared Corridor (Shpjegimi i zgjidhjes)

Solution

We need to count pairs of paths that have exactly one common contiguous segment containing at least two cells.

Each path can only move right or down. Let A=(1,a) and B=(b,1) be the starting cells, and let C=(c,w) and D=(h,d) be the destination cells.

The key observation is that every compatible pair of paths can be uniquely divided into three parts:

We will count these three parts using dynamic programming.

Counting paths

First, calculate four arrays:

All paths must use only free cells and move right or down.

The first two arrays can be calculated from top to bottom and from left to right. For example, for a free cell ,

F_A(i,j)=F_A(i-1,j)+F_A(i,j-1),

with the initial value F_A(A)=1.

Similarly, G_C and G_D can be calculated in reverse order:

G_C(i,j)=G_C(i+1,j)+G_C(i,j+1).

All values are calculated modulo 10^9+7.

First common cell

Let v=(i,j) be the first common cell of the two paths.

Since the paths do not intersect before , they must enter this cell from different directions.

Denote the cell above by U=(i-1,j) and the cell to its left by L=(i,j-1).

Because the first path starts on the upper boundary and the second path starts on the left boundary, the first path must enter from , and the second path must enter from .

We need to count pairs of paths from to and from to that do not share any cells.

Using the Lindström–Gessel–Viennot lemma, this number equals

join(v)=F_A(U)F_B(L)-F_A(L)F_B(U).

The subtraction removes pairs of paths that intersect before reaching . Due to the relative positions of the starting and ending cells, the opposite pairing cannot be vertex-disjoint.

For cells on the upper or left boundary, and for blocked cells, we set join(v)=0.

Thus, join(v) is the number of ways for the two couriers to meet for the first time at cell .

Last common cell

Now let v=(i,j) be the last common cell.

After leaving , the paths must immediately separate and never intersect again.

Denote the cell to the right of by R=(i,j+1) and the cell below it by T=(i+1,j).

Since the first courier finishes on the right boundary and the second courier finishes on the bottom boundary, the first path must leave through , while the second must leave through .

Using the same lemma, the number of ways to finish the two paths without further intersections is

split(v)=G_C(R)G_D(T)-G_C(T)G_D(R).

For cells on the right or bottom boundary, and for blocked cells, we set split(v)=0.

Shared segment

We have calculated the number of ways to start and finish the common segment. It remains to connect its endpoints.

Let be the number of ways to choose the first common cell and construct a shared path ending at , such that the shared path contains at least one edge.

For each cell , we can start a shared segment in ways.

However, we cannot immediately finish it at , because the common segment must contain at least two cells.

Therefore, these new segments contribute only when we move from to one of its neighboring cells.

For a free cell v=(i,j), let and again denote its upper and left neighbors.

Then

dp(v)=dp(U)+join(U)+dp(L)+join(L).

Here, and count extensions of existing shared segments, while join(U) and count shared segments that begin at the corresponding neighboring cell.

This guarantees that every counted shared segment contains at least one edge.

Finally, if the shared segment ends at , there are split(v) ways to finish both routes without further intersections.

Therefore, the answer is

ans=\sum_v dp(v)\cdot split(v)\pmod{10^9+7}.

Every compatible pair of routes has a unique first and last common cell, so it is counted exactly once.

Complexity Analysis

Each of the four path-counting arrays can be calculated in time.

The values join(v), split(v) and are also calculated in time per cell.

Therefore, the total time complexity is , and the space complexity is .

Implementation

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

using ll = long long;

const int mod = 1e9 + 7;

void solve() {
    int h, w;
    cin >> h >> w;

    int a, b, c, d;
    cin >> a >> b >> c >> d;

    --a; --b; --c; --d;

    vector<string> g(h);
    for (auto &s : g) cin >> s;

    int n = h * w;

    vector<int> fromA(n), fromB(n);
    vector<int> join(n), dp(n);
    vector<int> toC(n), toD(n);

    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            int v = i * w + j;

            if (g[i][j] == '#') continue;

            int up = v - w;
            int left = v - 1;

            ll x = (i == 0 && j == a);
            ll y = (i == b && j == 0);

            if (i) {
                x += fromA[up];
                y += fromB[up];
            }

            if (j) {
                x += fromA[left];
                y += fromB[left];
            }

            fromA[v] = x % mod;
            fromB[v] = y % mod;

            if (i && j) {
                int t = 1LL * fromA[up] * fromB[left] % mod;

                t -= 1LL * fromA[left] * fromB[up] % mod;

                if (t < 0) t += mod;

                join[v] = t;
            }

            ll z = 0;

            if (i) {
                z += dp[up] + join[up];
            }

            if (j) {
                z += dp[left] + join[left];
            }

            dp[v] = z % mod;
        }
    }

    int ans = 0;

    for (int i = h - 1; i >= 0; i--) {
        for (int j = w - 1; j >= 0; j--) {
            int v = i * w + j;

            if (g[i][j] == '#') continue;

            int down = v + w;
            int right = v + 1;

            ll x = (i == c && j == w - 1);
            ll y = (i == h - 1 && j == d);

            if (i + 1 < h) {
                x += toC[down];
                y += toD[down];
            }

            if (j + 1 < w) {
                x += toC[right];
                y += toD[right];
            }

            toC[v] = x % mod;
            toD[v] = y % mod;

            if (i + 1 < h && j + 1 < w) {
                int t = 1LL * toC[right] * toD[down] % mod;

                t -= 1LL * toC[down] * toD[right] % mod;

                if (t < 0) t += mod;

                ans = (ans + 1LL * dp[v] * t) % mod;
            }
        }
    }

    cout << ans << '\n';
}

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

    solve();
}