Smooth Sailing (Hard Version) (Codeforces 1920F2)
Tags: Others, geometry

The only difference between the two versions of this problem is the constraint on $$$q$$$. You can make hacks only if both versions of the problem are solved.Thomas is sailing around an island surrounded by the ocean. The ocean and island can be represented by a grid with $$$n$$$ rows and $$$m$$$ columns. The rows are numbered from $$$1$$$ to $$$n$$$ from top to bottom, and the columns are numbered from $$$1$$$ to $$$m$$$ from left to right. The position of a cell at row $$$r$$$ and column $$$c$$$ can be represented as $$$(r, c)$$$. Below is an example of a valid grid.  Example of a valid grid There are three types of cells: island, ocean and underwater volcano. Cells representing the island are marked with a '#', cells representing the ocean are marked with a '.', and cells representing an underwater volcano are marked with a 'v'. It is guaranteed that there is at least one island cell and at least one underwater volcano cell. It is also guaranteed that the set of all island cells forms a single connected component$$$^{\dagger}$$$ and the set of all ocean cells and underwater volcano cells forms a single connected component. Additionally, it is guaranteed that there are no island cells at the edge of the grid (that is, at row $$$1$$$, at row $$$n$$$, at column $$$1$$$, and at column $$$m$$$).Define a round trip starting from cell $$$(x, y)$$$ as a path Thomas takes which satisfies the following conditions:  The path starts and ends at $$$(x, y)$$$.  If Thomas is at cell $$$(i, j)$$$, he can go to cells $$$(i+1, j)$$$, $$$(i-1, j)$$$, $$$(i, j-1)$$$, and $$$(i, j+1)$$$ as long as the destination cell is an ocean cell or an underwater volcano cell and is still inside the grid. Note that it is allowed for Thomas to visit the same cell multiple times in the same round trip.  The path must go around the island and fully encircle it. Some path $$$p$$$ fully encircles the island if it is impossible to go from an island cell to a cell on the grid border by only traveling to adjacent on a side or diagonal cells without visiting a cell on path $$$p$$$. In the image below, the path starting from $$$(2, 2)$$$, going to $$$(1, 3)$$$, and going back to $$$(2, 2)$$$ the other way does not fully encircle the island and is not considered a round trip.   Example of a path that does not fully encircle the island The safety of a round trip is the minimum Manhattan distance$$$^{\ddagger}$$$ from a cell on the round trip to an underwater volcano (note that the presence of island cells does not impact this distance).You have $$$q$$$ queries. A query can be represented as $$$(x, y)$$$ and for every query, you want to find the maximum safety of a round trip starting from $$$(x, y)$$$. It is guaranteed that $$$(x, y)$$$ is an ocean cell or an underwater volcano cell.$$$^{\dagger}$$$A set of cells forms a single connected component if from any cell of this set it is possible to reach any other cell of this set by moving only through the cells of this set, each time going to a cell with a common side.$$$^{\ddagger}$$$Manhattan distance between cells $$$(r_1, c_1)$$$ and $$$(r_2, c_2)$$$ is equal to $$$|r_1 - r_2| + |c_1 - c_2|$$$.

Input: The first line contains three integers $$$n$$$, $$$m$$$, and $$$q$$$ ($$$3 \leq n, m \leq 10^5$$$, $$$9 \leq n \cdot m \leq 3 \cdot 10^5$$$, $$$1 \leq q \leq 3 \cdot 10^5$$$) — the number of rows and columns of the grid and the number of queries. Each of the following $$$n$$$ lines contains $$$m$$$ characters describing the cells of the grid. The character '#' denotes an island cell, '.' denotes an ocean cell, and 'v' denotes an underwater volcano cell.It is guaranteed that there is at least one island cell and at least one underwater volcano cell. It is guaranteed that the set of all island cells forms a single connected component and the set of all ocean cells and underwater volcano cells forms a single connected component. Also, it is guaranteed that there are no island cells at the edge of the grid (that is, at the row $$$1$$$, at the row $$$n$$$, at the column $$$1$$$, and at the column $$$m$$$).The following $$$q$$$ lines describe the queries. Each of these lines contains two integers $$$x$$$ and $$$y$$$ ($$$1 \leq x \leq n$$$, $$$1 \leq y \leq m$$$) denoting a round trip starting from $$$(x, y)$$$.It is guaranteed that $$$(x, y)$$$ is an ocean cell or an underwater volcano cell.

Output: For each query, output a single integer — the maximum safety of a round trip starting from the specified position.

Note: For the first example, the image below shows an optimal round trip starting from $$$(1, 1)$$$. The round trip has a safety of $$$3$$$ as the minimum Manhattan distance from a cell on the round trip to an underwater volcano is $$$3$$$.  Example of an optimal round trip For the fourth example, remember that it is allowed for Thomas to visit the same cell multiple times in the same round trip. For example, doing so is necessary for the round trip starting from $$$(7, 6)$$$.

Sample Inputs:
9 9 3
.........
.........
....###..
...v#....
..###....
...##...v
...##....
.........
v........
1 1
9 1
5 7

3 3 5
..v
.#.
...
1 2
1 3
2 3
2 1
3 2

14 13 5
.............
.............
.............
...vvvvvvv...
...v.....v...
...v.###.v...
...v.#.#.v...
...v..v..v...
...v..v..v...
....v...v....
.....vvv.....
.............
.............
.............
1 1
7 7
5 6
4 10
13 6

10 11 4
...........
..#######..
..#..#..#..
..#.....#..
..#..v..#..
..#.###.#..
..#.#.#.#..
..#...#.#..
..#####.#..
...........
7 6
3 7
6 8
1 1

Sample Outputs:
3
0
3

0
0
0
0
0

3
0
1
0
2

1
2
3
4

Solution (Python):
def getpd(x):
    if P[x] == x:
        return (x, 0)
    (p, d) = getpd(P[x])
    dd = D[x] + d
    P[x] = p
    D[x] = dd
    return (p, dd)
(n, m, q) = map(int, input().split())
a = [list(input()) for _ in range(n)]
v = [[-1] * m for _ in range(n)]
queue = []
(rx, ry) = (None, None)
for x in range(n):
    for y in range(m):
        if a[x][y] == 'v':
            queue.append((x, y))
            v[x][y] = 0
        elif a[x][y] == '#':
            (rx, ry) = (x, y)
i = 0
while i < len(queue):
    (x, y) = queue[i]
    i += 1
    for (xx, yy) in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
        if 0 <= xx < n and 0 <= yy < m and (v[xx][yy] < 0):
            v[xx][yy] = v[x][y] + 1
            queue.append((xx, yy))
P = list(range(n * m))
D = [0] * (n * m)
S = [1] * (n * m)
ans = [None] * (n * m)
C = [[i] for i in range(n * m)]
reach = [[False] * m for _ in range(n)]
for i in range(len(queue) - 1, -1, -1):
    (x, y) = queue[i]
    if a[x][y] == '#':
        continue
    reach[x][y] = True
    for (xx, yy) in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
        if 0 <= xx < n and 0 <= yy < m and reach[xx][yy]:
            (ap, ad) = getpd(x * m + y)
            (bp, bd) = getpd(xx * m + yy)
            asz = S[ap]
            bsz = S[bp]
            delta = y > ry and (x == rx and xx == rx - 1 or (x == rx - 1 and xx == rx))
            cl = None
            if ap != bp:
                if asz > bsz:
                    D[bp] = (delta + bd + ad) % 2
                    P[bp] = ap
                    S[ap] = asz + bsz
                    if not C[ap] or not C[bp]:
                        cl = ap
                    C[ap] = [C[ap], C[bp]]
                else:
                    D[ap] = (delta + bd + ad) % 2
                    P[ap] = bp
                    S[bp] = asz + bsz
                    if not C[ap] or not C[bp]:
                        cl = bp
                    C[bp] = [C[bp], C[ap]]
            elif (ad + bd + delta) % 2 == 1:
                cl = ap
            if cl is not None:
                vv = v[x][y]
                st = C[cl]
                while st:
                    u = st.pop()
                    if isinstance(u, list):
                        for uu in u:
                            st.append(uu)
                    else:
                        ans[u] = vv
res = []
for _ in range(q):
    (x, y) = map(int, input().split())
    res.append(str(ans[(x - 1) * m + (y - 1)]))
print('\n'.join(res))

Solution (Python):
import sys
import math

def solve():
    inp = sys.stdin.readline
    asin = math.asin
    (n, m, q) = map(int, inp().split())
    a = [None] * n
    for i in range(n):
        a[i] = list(inp())
    v = [[-1] * m for i in range(n)]
    queue = []
    (rx, ry) = (None, None)
    for x in range(n):
        for y in range(m):
            if a[x][y] == 'v':
                queue.append((x, y))
                v[x][y] = 0
            elif a[x][y] == '#':
                rx = x
                ry = y
    i = 0
    while i < len(queue):
        (x, y) = queue[i]
        i += 1
        for (xx, yy) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            if xx >= 0 and yy >= 0 and (xx < n) and (yy < m) and (v[xx][yy] < 0):
                v[xx][yy] = v[x][y] + 1
                queue.append((xx, yy))
    P = [i for i in range(n * m)]
    D = [0] * (n * m)
    S = [1] * (n * m)
    ans = [None] * (n * m)
    C = [[i] for i in range(n * m)]
    reach = [[False] * m for i in range(n)]

    def getpd(x):
        if P[x] == x:
            return (x, 0)
        (p, d) = getpd(P[x])
        dd = D[x] + d
        P[x] = p
        D[x] = dd
        return (p, dd)
    for i in range(len(queue) - 1, -1, -1):
        (x, y) = queue[i]
        if a[x][y] == '#':
            continue
        reach[x][y] = True
        for (xx, yy) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            if xx >= 0 and yy >= 0 and (xx < n) and (yy < m) and reach[xx][yy]:
                (ap, ad) = getpd(x * m + y)
                (bp, bd) = getpd(xx * m + yy)
                asz = S[ap]
                bsz = S[bp]
                delta = y > ry and (x == rx and xx == rx - 1 or (x == rx - 1 and xx == rx))
                cl = None
                if ap != bp:
                    if asz > bsz:
                        D[bp] = (delta + bd + ad) % 2
                        P[bp] = ap
                        S[ap] = asz + bsz
                        if not C[ap] or not C[bp]:
                            cl = ap
                        C[ap] = [C[ap], C[bp]]
                    else:
                        D[ap] = (delta + bd + ad) % 2
                        P[ap] = bp
                        S[bp] = asz + bsz
                        if not C[ap] or not C[bp]:
                            cl = bp
                        C[bp] = [C[bp], C[ap]]
                elif (ad + bd + delta) % 2 == 1:
                    cl = ap
                if cl is not None:
                    vv = v[x][y]
                    st = C[cl]
                    while st:
                        u = st.pop()
                        if type(u) is list:
                            for uu in u:
                                st.append(uu)
                        else:
                            ans[u] = vv
    res = [None] * q
    for i in range(q):
        (x, y) = map(int, inp().split())
        res[i] = str(ans[(x - 1) * m + (y - 1)])
    print('\n'.join(res))

def main():
    solve()
if __name__ == '__main__':
    main()

Solution (Python):
import sys
import math

def solve():
    inp = sys.stdin.readline
    asin = math.asin
    (n, m, q) = map(int, inp().split())
    a = [None] * n
    for i in range(n):
        a[i] = list(inp())
    v = [[-1] * m for i in range(n)]
    queue = []
    (rx, ry) = (None, None)
    for x in range(n):
        for y in range(m):
            if a[x][y] == 'v':
                queue.append((x, y))
                v[x][y] = 0
            elif a[x][y] == '#':
                rx = x
                ry = y
    i = 0
    while i < len(queue):
        (x, y) = queue[i]
        i += 1
        for (xx, yy) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            if xx >= 0 and yy >= 0 and (xx < n) and (yy < m) and (v[xx][yy] < 0):
                v[xx][yy] = v[x][y] + 1
                queue.append((xx, yy))
    P = [i for i in range(n * m)]
    D = [0] * (n * m)
    S = [1] * (n * m)
    ans = [None] * (n * m)
    C = [[i] for i in range(n * m)]
    reach = [[False] * m for i in range(n)]

    def getpd(x):
        if P[x] == x:
            return (x, 0)
        (p, d) = getpd(P[x])
        dd = D[x] + d
        P[x] = p
        D[x] = dd
        return (p, dd)
    for i in range(len(queue) - 1, -1, -1):
        (x, y) = queue[i]
        if a[x][y] == '#':
            continue
        reach[x][y] = True
        for (xx, yy) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            if xx >= 0 and yy >= 0 and (xx < n) and (yy < m) and reach[xx][yy]:
                (ap, ad) = getpd(x * m + y)
                (bp, bd) = getpd(xx * m + yy)
                asz = S[ap]
                bsz = S[bp]
                delta = y > ry and (x == rx and xx == rx - 1 or (x == rx - 1 and xx == rx))
                cl = None
                if ap != bp:
                    if asz > bsz:
                        D[bp] = (delta + bd + ad) % 2
                        P[bp] = ap
                        S[ap] = asz + bsz
                        if not C[ap] or not C[bp]:
                            cl = ap
                        C[ap] = [C[ap], C[bp]]
                    else:
                        D[ap] = (delta + bd + ad) % 2
                        P[ap] = bp
                        S[bp] = asz + bsz
                        if not C[ap] or not C[bp]:
                            cl = bp
                        C[bp] = [C[bp], C[ap]]
                elif (ad + bd + delta) % 2 == 1:
                    cl = ap
                if cl is not None:
                    vv = v[x][y]
                    st = C[cl]
                    while st:
                        u = st.pop()
                        if type(u) is list:
                            for uu in u:
                                st.append(uu)
                        else:
                            ans[u] = vv
    res = [None] * q
    for i in range(q):
        (x, y) = map(int, inp().split())
        res[i] = str(ans[(x - 1) * m + (y - 1)])
    print('\n'.join(res))

def main():
    solve()
if __name__ == '__main__':
    main()