公式

E - ロボット掃除機 / Robot Vacuum Cleaner 解説 by harurun4635


\(O(NMA)\) で以下を列挙できます。

  • 任意の \((x, y, i, t)\) について、\((x, y)\) から開始して、命令列 \(S_i\) の先頭 \(t ~ (0 \le t \le L_i)\) 文字を処理したときの位置 \((x', y')\)

これは、異なる \(S_i\) ごとに、すべてのマスを始点として愚直なシミュレーションをすればよいです。以下簡単のためこの写像を \((x', y') = f_t(x, y)\) と書きます。(以下 \(i\) は基本的に自明なので省略します)


さて、上記が得られたあとの各 \(i\) ごとの問題を考えます。 \(T = p \times L + q ~ (0 \le q \lt L)\) を用いれば、\(T\) ターン後の位置は以下のように求められます。

  • \((R', C') = {f_{L}}^p(R, C)\) ( \(L\) 文字処理するを \(p\) 回繰り返したもの)を求める。
  • \(f_q(R', C')\) を求める。(これが答え)

\(2\) つ目は前計算から \(O(1)\) で求める事ができますから、\(1\) つ目の求め方を考えましょう。


単純な関数の合成ですから、ダブリングを用いることができます。すなわち \(\displaystyle {f_L}^{2^k}\) を各マスについて前計算することを考えれば、 \(O(NM \log T)\) の前計算とクエリあたり \(O(\log T)\) で求めることができます。

また、遷移先が \(NM\) 個しか無いことから、それより短い周期が存在することを利用して、\(O(NM)\) の前計算とクエリあたり \(O(1)\) で求めることもできます。

計算量は、前者であれば \(O(NMA + NM|\mathcal S| \log T + K \log T)\) 、後者であれば \(O(NMA + K)\) です。


実装例(前者の方針で、かつメモリ削減のためダブリングテーブルを陽に管理せず、必要な箇所だけ計算しています)

from collections import defaultdict

n, m, k, t = map(int, input().split())
g = [input().strip() for _ in range(n)]

to = {c : list(range(n * m)) for c in "UDLR"}
for r in range(n):
    for c in range(m):
        if g[r][c] == '#': continue
        v = r * m + c
        if r > 0   and g[r-1][c] == '.': to['U'][v] = v-m
        if r+1 < n and g[r+1][c] == '.': to['D'][v] = v+m
        if c > 0   and g[r][c-1] == '.': to['L'][v] = v-1
        if c+1 < m and g[r][c+1] == '.': to['R'][v] = v+1

rob = defaultdict(list)
for i in range(k):
    r, c, _, s = input().split()
    rob[s].append(((int(r) - 1) * m + int(c) - 1, i))

ans = [0] * k

for s, qs in rob.items():
    p, q = divmod(t, len(s))

    f = list(range(n * m))
    for i, c in enumerate(s):
        if i == q:
            fq = f[:]
        f = [to[c][v] for v in f]
    while p:
        if p & 1:
            qs = [(f[v], i) for v, i in qs]
        f = [f[v] for v in f]
        p >>= 1

    for v, i in qs:
        ans[i] = fq[v]

for v in ans:
    print(v // m + 1, v % m + 1)

投稿日時:
最終更新: