This documentation is automatically generated by online-judge-tools/verification-helper
# verification-helper: PROBLEM https://atcoder.jp/contests/abc246/tasks/abc246_e
def solve():
N = read(int)
Ax, Ay = read(tuple[-1, ...])
Bx, By = read(tuple[-1, ...])
G = read(BishopBoard[N,N])
if (Ax+Ay)&1 != (Bx+By)&1:
return -1
s,g = G.vertex((Ax, Ay)), G.vertex((Bx, By))
ans = G.distance(s, g)
return -1 if ans == inf else ans
def main():
write(solve())
from collections import deque
from math import inf
from typing import Iterable
from cp_library.alg.graph.lazy_grid_direction_graph_cls import LazyGridDirectionGraph
from cp_library.io.read_fn import read
from cp_library.io.write_fn import write
class BishopBoard(LazyGridDirectionGraph):
def __init__(G, H, W, S=...):
dirs = [(1,1),(1,-1),(-1,1),(-1,-1)]
super().__init__(H, W, S, dirs)
def free_move(G, v: int, dir: int) -> Iterable[int]:
if dir < 0: return v
H, W = G.H, G.W
i,j = divmod(v, W)
di,dj = G.dirs[dir]
ni,nj = i+di,j+dj
if G.is_valid(ni, nj, u := ni*W+nj):
return u
return v
def bfs(G, s = 0, g = None):
D = [[inf]*4 for _ in range(G.N)]
D[s] = [0]*4
q = deque([(s,-1)])
while q:
u, dir = q.popleft()
if u == g: return D[u][dir]
nd = D[u][dir]
if nd < D[v := G.free_move(u,dir)][dir]:
D[v][dir] = nd
q.appendleft((v,dir))
nd += 1
for v, ndir in G[u]:
if nd < D[v][ndir]:
D[v][ndir] = nd
q.append((v,ndir))
return D if g is None else inf
if __name__ == "__main__":
main()
# verification-helper: PROBLEM https://atcoder.jp/contests/abc246/tasks/abc246_e
def solve():
N = read(int)
Ax, Ay = read(tuple[-1, ...])
Bx, By = read(tuple[-1, ...])
G = read(BishopBoard[N,N])
if (Ax+Ay)&1 != (Bx+By)&1:
return -1
s,g = G.vertex((Ax, Ay)), G.vertex((Bx, By))
ans = G.distance(s, g)
return -1 if ans == inf else ans
def main():
write(solve())
from collections import deque
from math import inf
from typing import Iterable
'''
╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
https://kobejean.github.io/cp-library
'''
from collections.abc import Iterator
import sys
import typing
from numbers import Number
from types import GenericAlias
from typing import Callable, Collection, Iterator, Union
import os
from io import BytesIO, IOBase
class FastIO(IOBase):
BUFSIZE = 8192
newlines = 0
def __init__(self, file):
self._fd = file.fileno()
self.buffer = BytesIO()
self.writable = "x" in file.mode or "r" not in file.mode
self.write = self.buffer.write if self.writable else None
def read(self):
BUFSIZE = self.BUFSIZE
while True:
b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
if not b:
break
ptr = self.buffer.tell()
self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
self.newlines = 0
return self.buffer.read()
def readline(self):
BUFSIZE = self.BUFSIZE
while self.newlines == 0:
b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
self.newlines = b.count(b"\n") + (not b)
ptr = self.buffer.tell()
self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
self.newlines -= 1
return self.buffer.readline()
def flush(self):
if self.writable:
os.write(self._fd, self.buffer.getvalue())
self.buffer.truncate(0), self.buffer.seek(0)
class IOWrapper(IOBase):
stdin: 'IOWrapper' = None
stdout: 'IOWrapper' = None
def __init__(self, file):
self.buffer = FastIO(file)
self.flush = self.buffer.flush
self.writable = self.buffer.writable
def write(self, s):
return self.buffer.write(s.encode("ascii"))
def read(self):
return self.buffer.read().decode("ascii")
def readline(self):
return self.buffer.readline().decode("ascii")
sys.stdin = IOWrapper.stdin = IOWrapper(sys.stdin)
sys.stdout = IOWrapper.stdout = IOWrapper(sys.stdout)
from typing import TypeVar
_T = TypeVar('T')
class TokenStream(Iterator):
stream = IOWrapper.stdin
def __init__(self):
self.queue = deque()
def __next__(self):
if not self.queue: self.queue.extend(self._line())
return self.queue.popleft()
def wait(self):
if not self.queue: self.queue.extend(self._line())
while self.queue: yield
def _line(self):
return TokenStream.stream.readline().split()
def line(self):
if self.queue:
A = list(self.queue)
self.queue.clear()
return A
return self._line()
TokenStream.default = TokenStream()
class CharStream(TokenStream):
def _line(self):
return TokenStream.stream.readline().rstrip()
CharStream.default = CharStream()
ParseFn = Callable[[TokenStream],_T]
class Parser:
def __init__(self, spec: Union[type[_T],_T]):
self.parse = Parser.compile(spec)
def __call__(self, ts: TokenStream) -> _T:
return self.parse(ts)
@staticmethod
def compile_type(cls: type[_T], args = ()) -> _T:
if issubclass(cls, Parsable):
return cls.compile(*args)
elif issubclass(cls, (Number, str)):
def parse(ts: TokenStream): return cls(next(ts))
return parse
elif issubclass(cls, tuple):
return Parser.compile_tuple(cls, args)
elif issubclass(cls, Collection):
return Parser.compile_collection(cls, args)
elif callable(cls):
def parse(ts: TokenStream):
return cls(next(ts))
return parse
else:
raise NotImplementedError()
@staticmethod
def compile(spec: Union[type[_T],_T]=int) -> ParseFn[_T]:
if isinstance(spec, (type, GenericAlias)):
cls = typing.get_origin(spec) or spec
args = typing.get_args(spec) or tuple()
return Parser.compile_type(cls, args)
elif isinstance(offset := spec, Number):
cls = type(spec)
def parse(ts: TokenStream): return cls(next(ts)) + offset
return parse
elif isinstance(args := spec, tuple):
return Parser.compile_tuple(type(spec), args)
elif isinstance(args := spec, Collection):
return Parser.compile_collection(type(spec), args)
elif isinstance(fn := spec, Callable):
def parse(ts: TokenStream): return fn(next(ts))
return parse
else:
raise NotImplementedError()
@staticmethod
def compile_line(cls: _T, spec=int) -> ParseFn[_T]:
if spec is int:
fn = Parser.compile(spec)
def parse(ts: TokenStream): return cls([int(token) for token in ts.line()])
return parse
else:
fn = Parser.compile(spec)
def parse(ts: TokenStream): return cls([fn(ts) for _ in ts.wait()])
return parse
@staticmethod
def compile_repeat(cls: _T, spec, N) -> ParseFn[_T]:
fn = Parser.compile(spec)
def parse(ts: TokenStream): return cls([fn(ts) for _ in range(N)])
return parse
@staticmethod
def compile_children(cls: _T, specs) -> ParseFn[_T]:
fns = tuple((Parser.compile(spec) for spec in specs))
def parse(ts: TokenStream): return cls([fn(ts) for fn in fns])
return parse
@staticmethod
def compile_tuple(cls: type[_T], specs) -> ParseFn[_T]:
if isinstance(specs, (tuple,list)) and len(specs) == 2 and specs[1] is ...:
return Parser.compile_line(cls, specs[0])
else:
return Parser.compile_children(cls, specs)
@staticmethod
def compile_collection(cls, specs):
if not specs or len(specs) == 1 or isinstance(specs, set):
return Parser.compile_line(cls, *specs)
elif (isinstance(specs, (tuple,list)) and len(specs) == 2 and isinstance(specs[1], int)):
return Parser.compile_repeat(cls, specs[0], specs[1])
else:
raise NotImplementedError()
class Parsable:
@classmethod
def compile(cls):
def parser(ts: TokenStream): return cls(next(ts))
return parser
from enum import auto, IntFlag, IntEnum
class DFSFlags(IntFlag):
ENTER = auto()
DOWN = auto()
BACK = auto()
CROSS = auto()
LEAVE = auto()
UP = auto()
MAXDEPTH = auto()
RETURN_PARENTS = auto()
RETURN_DEPTHS = auto()
BACKTRACK = auto()
CONNECT_ROOTS = auto()
# Common combinations
ALL_EDGES = DOWN | BACK | CROSS
EULER_TOUR = DOWN | UP
INTERVAL = ENTER | LEAVE
TOPDOWN = DOWN | CONNECT_ROOTS
BOTTOMUP = UP | CONNECT_ROOTS
RETURN_ALL = RETURN_PARENTS | RETURN_DEPTHS
class DFSEvent(IntEnum):
ENTER = DFSFlags.ENTER
DOWN = DFSFlags.DOWN
BACK = DFSFlags.BACK
CROSS = DFSFlags.CROSS
LEAVE = DFSFlags.LEAVE
UP = DFSFlags.UP
MAXDEPTH = DFSFlags.MAXDEPTH
def elist(est_len: int) -> list: ...
try:
from __pypy__ import newlist_hint
except:
def newlist_hint(hint):
return []
elist = newlist_hint
from typing import Iterable, Union, overload
class GraphProtocol(list, Parsable):
def __init__(G, N: int, E: list = None, adj: Iterable = None):
G.N = N
if E is not None:
G.M, G.E = len(E), E
if adj is not None:
super().__init__(adj)
def neighbors(G, v: int) -> Iterable[int]:
return G[v]
def edge_ids(G) -> list[list[int]]: ...
@overload
def distance(G) -> list[list[int]]: ...
@overload
def distance(G, s: int = 0) -> list[int]: ...
@overload
def distance(G, s: int, g: int) -> int: ...
def distance(G, s = None, g = None):
if s == None:
return G.floyd_warshall()
else:
return G.bfs(s, g)
@overload
def bfs(G, s: Union[int,list] = 0) -> list[int]: ...
@overload
def bfs(G, s: Union[int,list], g: int) -> int: ...
def bfs(G, s = 0, g = None):
D = [inf for _ in range(G.N)]
q = deque([s] if isinstance(s, int) else s)
for u in q: D[u] = 0
while q:
nd = D[u := q.popleft()]+1
if u == g: return D[u]
for v in G.neighbors(u):
if nd < D[v]:
D[v] = nd
q.append(v)
return D if g is None else inf
@overload
def shortest_path(G, s: int, g: int) -> Union[list[int],None]: ...
@overload
def shortest_path(G, s: int, g: int, distances = True) -> tuple[Union[list[int],None],list[int]]: ...
def shortest_path(G, s: int, g: int, distances = False) -> list[int]:
D = [inf] * G.N
D[s] = 0
if s == g:
return ([], D) if distances else []
par = [-1] * G.N
par_edge = [-1] * G.N
Eid = G.edge_ids()
q = deque([s])
while q:
nd = D[u := q.popleft()] + 1
if u == g: break
for v, eid in zip(G[u], Eid[u]):
if nd < D[v]:
D[v] = nd
par[v] = u
par_edge[v] = eid
q.append(v)
if D[g] == inf:
return (None, D) if distances else None
path = []
current = g
while current != s:
path.append(par_edge[current])
current = par[current]
return (path[::-1], D) if distances else path[::-1]
def floyd_warshall(G) -> list[list[int]]:
D = [[inf]*G.N for _ in range(G.N)]
for u in range(G.N):
D[u][u] = 0
for v in G.neighbors(u):
D[u][v] = 1
for k, Dk in enumerate(D):
for Di in D:
if Di[k] == inf: continue
for j in range(G.N):
if Dk[j] == inf: continue
Di[j] = min(Di[j], Di[k]+Dk[j])
return D
def find_cycle(G, s = 0, vis = None, par = None):
N = G.N
vis = vis or [0] * N
par = par or [-1] * N
if vis[s]: return None
vis[s] = 1
stack = [(True, s)]
while stack:
forw, v = stack.pop()
if forw:
stack.append((False, v))
vis[v] = 1
for u in G.neighbors(v):
if vis[u] == 1 and u != par[v]:
# Cycle detected
cyc = [u]
vis[u] = 2
while v != u:
cyc.append(v)
vis[v] = 2
v = par[v]
return cyc
elif vis[u] == 0:
par[u] = v
stack.append((True, u))
else:
vis[v] = 2
return None
def find_minimal_cycle(G, s=0):
D, par, que = [inf] * (N := G.N), [-1] * N, deque([s])
D[s] = 0
while que:
for v in G[u := que.popleft()]:
if v == s: # Found cycle back to start
cycle = [u]
while u != s: cycle.append(u := par[u])
return cycle
if D[v] < inf: continue
D[v], par[v] = D[u]+1, u
que.append(v)
def bridges(G):
tin = [-1] * G.N
low = [-1] * G.N
par = [-1] * G.N
vis = [0] * G.N
in_edge = [-1] * G.N
Eid = G.edge_ids()
time = 0
bridges = []
stack = list(range(G.N))
while stack:
p = par[v := stack.pop()]
if vis[v] == 0:
vis[v] = 1
tin[v] = low[v] = time
time += 1
stack.append(v)
for i, child in enumerate(G.neighbors(v)):
if child == p: continue
if vis[child] == 0: # Tree edge - recurse
par[child] = v
in_edge[child] = Eid[v][i]
stack.append(child)
else: # Back edge - update low-link value
low[v] = min(low[v], tin[child])
elif vis[v] == 1:
vis[v] = 2
if p != -1:
low[p] = min(low[p], low[v])
if low[v] > tin[p]: bridges.append(in_edge[v])
return bridges
def articulation_points(G):
'''
Find articulation points in an undirected graph using DFS events.
Returns a boolean list that is True for indices where the vertex is an articulation point.
'''
N = G.N
order = [-1] * N
low = [-1] * N
par = [-1] * N
state = [0] * N
children = [0] * N
ap = [False] * N
time = 0
stack = list(range(N))
while stack:
v = stack.pop()
p = par[v]
if state[v] == 0:
state[v] = 1
order[v] = low[v] = time
time += 1
stack.append(v)
for child in G[v]:
if order[child] == -1:
par[child] = v
stack.append(child)
elif child != p:
low[v] = min(low[v], order[child])
if p != -1:
children[p] += 1
elif state[v] == 1:
state[v] = 2
ap[v] |= p == -1 and children[v] > 1
if p != -1:
low[p] = min(low[p], low[v])
ap[p] |= par[p] != -1 and low[v] >= order[p]
return ap
def dfs_events(G, flags: DFSFlags, s: Union[int,list,None] = None, max_depth: Union[int,None] = None):
if flags == DFSFlags.INTERVAL:
if max_depth is None:
return G.dfs_enter_leave(s)
elif flags == DFSFlags.DOWN or flags == DFSFlags.TOPDOWN:
if max_depth is None:
edges = G.dfs_topdown(s, DFSFlags.CONNECT_ROOTS in flags)
return [(DFSEvent.DOWN, p, u) for p,u in edges]
elif flags == DFSFlags.UP or flags == DFSFlags.BOTTOMUP:
if max_depth is None:
edges = G.dfs_bottomup(s, DFSFlags.CONNECT_ROOTS in flags)
return [(DFSEvent.UP, p, u) for p,u in edges]
elif flags & DFSFlags.BACKTRACK:
return G.dfs_backtrack(flags, s, max_depth)
state = [0] * G.N
child = [0] * G.N
stack = [0] * G.N
if flags & DFSFlags.RETURN_PARENTS:
parents = [-1] * G.N
if flags & DFSFlags.RETURN_DEPTHS:
depths = [-1] * G.N
events = []
for s in G.starts(s):
stack[depth := 0] = s
if (DFSFlags.DOWN|DFSFlags.CONNECT_ROOTS) in flags:
events.append((DFSEvent.DOWN,-1,s))
while depth != -1:
u = stack[depth]
if not state[u]:
state[u] = 1
if flags & DFSFlags.ENTER:
events.append((DFSEvent.ENTER, u))
if flags & DFSFlags.RETURN_DEPTHS:
depths[u] = depth
if (c := child[u]) < len(G[u]):
child[u] += 1
if (s := state[v := G[u][c]]) == 0: # Unvisited
if max_depth is None or depth <= max_depth:
if flags & DFSFlags.DOWN:
events.append((DFSEvent.DOWN, u, v))
stack[depth := depth+1] = v
if flags & DFSFlags.RETURN_PARENTS:
parents[v] = u
elif s == 1: # In progress
if flags & DFSFlags.BACK:
events.append((DFSEvent.BACK, u, v))
elif s == 2: # Completed
if flags & DFSFlags.CROSS:
events.append((DFSEvent.CROSS, u, v))
else:
depth -= 1
state[u] = 0 if DFSFlags.BACKTRACK in flags else 2
if flags & DFSFlags.LEAVE:
events.append((DFSEvent.LEAVE, u))
if depth != -1 and flags & DFSFlags.UP:
events.append((DFSEvent.UP, stack[depth], u))
if (DFSFlags.UP|DFSFlags.CONNECT_ROOTS) in flags:
events.append((DFSEvent.UP,-1,s))
ret = tuple((events,)) if DFSFlags.RETURN_ALL & flags else events
if DFSFlags.RETURN_PARENTS in flags:
ret += (parents,)
if DFSFlags.RETURN_DEPTHS in flags:
ret += (depths,)
return ret
def dfs_backtrack(G, flags: DFSFlags, s: Union[int,list] = None, max_depth: Union[int,None] = None):
stack_depth = (max_depth+1 if max_depth is not None else G.N)
stack = [0]*stack_depth
child = [0]*stack_depth
state = [0]*G.N
events: list[tuple[DFSEvent, int]|tuple[DFSEvent, int, int]] = []
for s in G.starts(s):
if state[s]: continue
state[s] = 1
stack[depth := 0] = s
if DFSFlags.DOWN|DFSFlags.CONNECT_ROOTS in flags:
events.append((DFSEvent.DOWN,-1,s))
while depth != -1:
u = stack[depth]
if state[u] == 1:
state[u] = 2
if DFSFlags.ENTER in flags:
events.append((DFSEvent.ENTER,u))
if max_depth is not None and depth >= max_depth:
child[depth] = len(G[u])
if DFSFlags.MAXDEPTH in flags:
events.append((DFSEvent.MAXDEPTH,u))
if (c := child[depth]) < len(G[u]):
child[depth] += 1
if state[v := G[u][c]]:
if DFSFlags.BACK in flags:
events.append((DFSEvent.BACK,u,v))
continue
state[v] = 1
if DFSFlags.DOWN in flags:
events.append((DFSEvent.DOWN,u,v))
stack[depth := depth+1] = v
else:
state[u] = 0
if DFSFlags.LEAVE in flags:
events.append((DFSEvent.LEAVE,u))
child[depth] = 0
depth -= 1
if depth and DFSFlags.UP in flags:
events.append((DFSEvent.UP, stack[depth], u))
if DFSFlags.UP|DFSFlags.CONNECT_ROOTS in flags:
events.append((DFSEvent.UP,-1,s))
return events
def dfs_enter_leave(G, s: Union[int,list,None] = None):
state = [True] * G.N
child: list[int] = elist(G.N)
stack: list[int] = elist(G.N)
events = []
for s in G.starts(s):
if not state[s]: continue
stack.append(s)
child.append(0)
while stack:
u = stack[-1]
if state[u]:
state[u] = False
events.append((DFSEvent.ENTER, u))
if (c := child[-1]) < len(G[u]):
child[-1] += 1
if state[v := G[u][c]]:
stack.append(v)
child.append(0)
else:
stack.pop()
child.pop()
events.append((DFSEvent.LEAVE, u))
return events
def dfs_topdown(G, s: Union[int,list,None] = None, connect_roots = False):
'''Returns list of (u,v) representing u->v edges in order of top down discovery'''
stack: list[int] = elist(G.N)
vis = [False]*G.N
edges: list[tuple[int,int]] = elist(G.N)
for s in G.starts(s):
if vis[s]: continue
if connect_roots:
edges.append((-1,s))
vis[s] = True
stack.append(s)
while stack:
u = stack.pop()
for v in G[u]:
if vis[v]: continue
vis[v] = True
edges.append((u,v))
stack.append(v)
return edges
def dfs_bottomup(G, s: Union[int,list,None] = None, connect_roots = False):
'''Returns list of (p,u) representing p->u edges in bottom up order'''
edges = G.dfs_topdown(s, connect_roots)
edges.reverse()
return edges
def is_bipartite(G):
N = G.N
que = deque()
color = [-1]*N
for s in range(N):
if color[s] >= 0:
continue
color[s] = 1
que.append(s)
while que:
u = que.popleft()
for v in G[u]:
if color[v] == -1:
color[v] = 1 - color[u]
que.append(v)
elif color[v] == color[u]:
return False
return True
def starts(G, v: Union[int,list,None]) -> Iterable:
if isinstance(v, int):
return (v,)
elif v is None:
return range(G.N)
else:
return v
@classmethod
def compile(cls, N: int, M: int, E):
edge = Parser.compile(E)
def parse(ts: TokenStream):
return cls(N, [edge(ts) for _ in range(M)])
return parse
class GridGraphProtocol(GraphProtocol):
def __init__(G, H, W, S=str, dirs = [(-1,0),(0,1),(1,0),(0,-1)], wall = '#', adj = None):
super().__init__(W*H, None, adj)
G.W = W
G.H = H
G.S = S
G.dirs = dirs
G.wall = wall
def vertex(G, key: tuple[int,int] | int):
if isinstance(key, tuple):
i,j = key
return i*G.W+j
else:
return key
def is_valid(G, i, j, v):
return 0 <= i < G.H and 0 <= j < G.W and G.S[v] != G.wall
@classmethod
def compile(cls, H: int, W: int, *args):
def parse(ts: TokenStream):
S = ''.join(ts.stream.readline().rstrip() for _ in range(H))
return cls(H, W, S, *args)
return parse
class LazyGridGraph(GridGraphProtocol):
def neighbors(G, u: int) -> Iterable[int]:
S, wall, dirs, H, W = G.S, G.wall, G.dirs, G.H, G.W
i,j = divmod(u, W)
return tuple(v
for di,dj in dirs
if (0 <= (ni:=i+di) < H
and 0 <= (nj:=j+dj) < W
and S[v:=ni*W+nj] != wall)
) if S[u] != wall else tuple()
def __len__(G) -> int:
return G.N
def __getitem__(G, v: int):
return G.neighbors(v)
def __iter__(G) -> Iterator:
return iter(G[v] for v in range(G.N))
class LazyGridDirectionGraph(LazyGridGraph):
def neighbors(G, u: int) -> tuple[tuple[int,int], ...]:
S, wall, dirs, H, W = G.S, G.wall, G.dirs, G.H, G.W
i,j = divmod(u, W)
return tuple((v,ndir)
for ndir,(di,dj) in enumerate(dirs)
if (0 <= (ni:=i+di) < H
and 0 <= (nj:=j+dj) < W
and S[v:=ni*W+nj] != wall)
) if S[u] != wall else tuple()
from typing import Iterable, Type, Union, overload
@overload
def read() -> Iterable[int]: ...
@overload
def read(spec: int) -> list[int]: ...
@overload
def read(spec: Union[Type[_T],_T], char=False) -> _T: ...
def read(spec: Union[Type[_T],_T] = None, char=False):
if not char and spec is None: return map(int, TokenStream.default.line())
parser: _T = Parser.compile(spec)
return parser(CharStream.default if char else TokenStream.default)
def write(*args, **kwargs):
'''Prints the values to a stream, or to stdout_fast by default.'''
sep, file = kwargs.pop("sep", " "), kwargs.pop("file", IOWrapper.stdout)
at_start = True
for x in args:
if not at_start:
file.write(sep)
file.write(str(x))
at_start = False
file.write(kwargs.pop("end", "\n"))
if kwargs.pop("flush", False):
file.flush()
class BishopBoard(LazyGridDirectionGraph):
def __init__(G, H, W, S=...):
dirs = [(1,1),(1,-1),(-1,1),(-1,-1)]
super().__init__(H, W, S, dirs)
def free_move(G, v: int, dir: int) -> Iterable[int]:
if dir < 0: return v
H, W = G.H, G.W
i,j = divmod(v, W)
di,dj = G.dirs[dir]
ni,nj = i+di,j+dj
if G.is_valid(ni, nj, u := ni*W+nj):
return u
return v
def bfs(G, s = 0, g = None):
D = [[inf]*4 for _ in range(G.N)]
D[s] = [0]*4
q = deque([(s,-1)])
while q:
u, dir = q.popleft()
if u == g: return D[u][dir]
nd = D[u][dir]
if nd < D[v := G.free_move(u,dir)][dir]:
D[v][dir] = nd
q.appendleft((v,dir))
nd += 1
for v, ndir in G[u]:
if nd < D[v][ndir]:
D[v][ndir] = nd
q.append((v,ndir))
return D if g is None else inf
if __name__ == "__main__":
main()