A - Brightness Control Editorial /

Time Limit: 2 sec / Memory Limit: 1024 MiB

ストーリー

高橋君は、舞台照明の明るさを自動で調整する装置を開発している。

この装置では、格子状の盤面に反射板、分光板、吸収ブロックを配置することで、入射した光の進み方を制御できる。反射板は光の向きを変え、分光板は光を 2 方向に分け、吸収ブロックは光を消滅させる。

これから順番に必要となる明るさの列が事前に与えられている。盤面上の部品をうまく操作しながら光を発射し、できるだけ目標に近い明るさの列を実現せよ。

問題文

N \times N のグリッドがある。左上のマスの座標を (0, 0) とし、下方向に i マス、右方向に j マス進んだ位置の座標を (i, j) とする。

各マスには、高々 1 つの物体を設置できる。設置できる物体は以下のいずれかである。

  • 左下と右上を結ぶ反射板 /
  • 左上と右下を結ぶ反射板 \
  • 左下と右上を結ぶ分光板 Z
  • 左上と右下を結ぶ分光板 N
  • 吸収ブロック #

なお、分光板 ZN では、文字中の斜め線の向きが分光板の向きに対応している。 物体が設置されていないマスを空マスと呼ぶ。

初期盤面は自由に設定してよい。初期盤面の設定は操作ターン数に含まれない。

マス (0, N/2) の上辺には光線の発射装置がある。この装置から、強さ 1、鮮明度 60 の光線を下向きに発射できる。

発射された光線は、以下のように進行方向・強さ・鮮明度を変えながら、盤面上を進む。

空マス: 光線が空マスに入った場合、光線はそのまま直進する。

吸収ブロック: 光線が吸収ブロックに入った場合、その光線は消滅する。

反射板: 光線が反射板に入った場合、光線は 90 度向きを変えて反射される。反射方向は、入射する直前の光線の進行方向と反射板の向きによって次の表のように定まる。

入射前の進行方向 反射板 / 反射板 \

分光板: 光線が分光板に入った場合、鮮明度が正なら、光線は直進する光線と、分光板と同じ向きの反射板で反射された場合と同じ方向へ進む光線の 2 本に分かれる。分かれた後の 2 本の光線の強さは、それぞれ入射した光線の半分であり、鮮明度は入射した光線より 1 小さい。 一方、鮮明度が 0 の光線がさらに分光板に入ると、光は散乱し、装置の出力に予期しない影響を与えてしまう。散乱した光線はそれ以上追跡せず、出力値には加算されない。1 回の発射において、散乱したすべての光線の強さの合計を散乱値とする。

出力: 光線がグリッドの外へ出た場合、その光線は出力されたものとみなす。グリッドのどの辺から外へ出てもよい。1 回の発射において、グリッドの外へ出たすべての光線の強さの合計を出力値とする。

example

実線は反射板、破線は分光板、黒マスは吸収ブロックを表す。 上部から入射した光は、半分がそのままグリッドの外へ出力され、残りの半分が右下のループに入る。 ループに入った光は、その半分が吸収され、1/4 がグリッドの外へ出力され、残りの 1/4 が再びループに入る。 これを繰り返すことで、最終的に出力値は \frac{2}{3} - \frac{1}{3}\cdot 2^{-59}、散乱値は 2^{-60} となる。

あなたは初期盤面を設定したあと、以下の操作を最大で T ターンまで行うことができる。

  • 空マスに物体を設置する。
  • マスに設置されている物体を除去する。
  • 反射板または分光板 1 つを回転する。これにより /\ が、NZ がそれぞれ入れ替わる。
  • 光を発射する。

目標となる光の強さの列 A_0, A_1, \ldots, A_{L-1} が与えられる。あなたは光の発射操作をちょうど L 回行わなければならない。

k 回目の光の発射によって得られた出力値を B_k、散乱値を R_k とする。AB の誤差、および散乱値の総和をできるだけ小さくせよ。

出力値と散乱値に関する補足

分光板によるループを含む盤面では、光線が無限に分岐・循環しうる。このような場合でも、連立一次方程式を解くことで出力値を厳密に定義することはできる。しかし、実数値を用いた計算では、計算誤差によりスコアが変わる可能性がある。

そこで本問題では、各光線に鮮明度を持たせ、分光板を通る回数を高々 60 回に制限している。鮮明度が 0 の光線がさらに分光板に入った場合、その光線は散乱し、それ以上追跡されない。このため、1 回の発射における処理は必ず有限回で終了する。

また、計算上は強さ 2^{60} の光を入射させたと考えると、分光板を通るたびに強さが半分になるだけなので、すべての光線の強さを整数で扱うことができる。実際の出力値および散乱値は、それぞれ得られた整数値を 2^{60} で割った値である。

Python による出力値・散乱値計算の実装例
from collections import defaultdict

# Directions: 0=up, 1=right, 2=down, 3=left
DI = [-1, 0, 1, 0]
DJ = [0, 1, 0, -1]

INITIAL_CLARITY = 60
UNIT = 1 << INITIAL_CLARITY


def reflect_dir(ch, d):
    if ch in ("/", "Z"):
        return [1, 0, 3, 2][d]
    if ch in ("\\", "N"):
        return [3, 2, 1, 0][d]
    raise ValueError("not a mirror or splitter")


def calc_B_R(board):
    """
    現在の盤面で光を 1 回発射したときの (B, R) を返す。

    B は出力値、R は散乱値である。
    いずれも 1 / 2^60 を単位とする整数値として返す。
    """
    n = len(board)
    memo = {}

    def advance(i, j, d):
        """
        空マスと反射板をたどり、グリッド外、吸収ブロック、
        または分光板に到達するまで進める。
        """
        path = []

        while True:
            key = (i, j, d)
            if key in memo:
                res = memo[key]
                break

            path.append(key)

            ch = board[i][j]

            if ch == "#":
                res = ("absorb", None)
                break

            if ch in ("Z", "N"):
                res = ("split", (i, j, d))
                break

            if ch == ".":
                nd = d
            elif ch in ("/", "\\"):
                nd = reflect_dir(ch, d)
            else:
                raise ValueError("invalid board character")

            ni = i + DI[nd]
            nj = j + DJ[nd]

            if not (0 <= ni < n and 0 <= nj < n):
                res = ("out", None)
                break

            i, j, d = ni, nj, nd

        for key in path:
            memo[key] = res

        return res

    cur = defaultdict(int)
    cur[(0, n // 2, 2)] = UNIT

    B = 0
    R = 0

    for clarity in range(INITIAL_CLARITY, -1, -1):
        nxt = defaultdict(int)

        for (i, j, d), amount in cur.items():
            kind, arg = advance(i, j, d)

            if kind == "out":
                B += amount

            elif kind == "absorb":
                pass

            elif kind == "split":
                if clarity == 0:
                    R += amount
                    continue

                si, sj, sd = arg
                half = amount // 2

                for nd in (sd, reflect_dir(board[si][sj], sd)):
                    ni = si + DI[nd]
                    nj = sj + DJ[nd]

                    if not (0 <= ni < n and 0 <= nj < n):
                        B += half
                    else:
                        nxt[(ni, nj, nd)] += half

        cur = nxt

    return B, R

得点

k 回目の光の発射によって得られた出力値を B_k、散乱値を R_k とする。誤差 E を以下で定義する。

\[ E = \sum_{k=0}^{L-1} \left(|A_k - B_k| + R_k\right) \]

このとき、以下の絶対スコアが得られる。

\[ 1 + \mathrm{round}(10^9 \times E) \]

絶対スコアは小さければ小さいほど良い。

各テストケースごとに、\mathrm{round}(10^9\times \frac{全参加者中の最小絶対スコア}{自身の絶対スコア})相対評価スコアが得られ、その和が提出の得点となる。

最終順位はコンテスト終了後に実施される、より多くの入力に対するシステムテストにおける得点で決定される。 暫定テスト、システムテストともに、一部のテストケースで不正な出力や制限時間超過をした場合、そのテストケースの相対評価スコアは 0 点となり、そのテストケースにおいては「全参加者中の最小絶対スコア」の計算から除外される。 システムテストはCE 以外の結果を得た一番最後の提出に対してのみ行われるため、最終的に提出する解答を間違えないよう注意せよ。

テストケース数

  • 暫定テスト: 50 個
  • システムテスト: 2000 個、コンテスト終了後に seeds.txt (sha256=09a07ffd9ee0e93469394a2cb36a22a9b4be312865de69d90d27198173124f04) を公開

相対評価システムについて

暫定テスト、システムテストともに、CE 以外の結果を得た一番最後の提出のみが順位表に反映される。 相対評価スコアの計算に用いられる各テストケースごとの全参加者中の最小絶対スコアの算出においても、順位表に反映されている最終提出のみが用いられる。

順位表に表示されているスコアは相対評価スコアであり、新規提出があるたびに、相対評価スコアが再計算される。 一方、提出一覧から確認できる各提出のスコアは各テストケースごとの絶対スコアをそのまま足し合わせたものであり、相対評価スコアは表示されない。 最新以外の提出について、現在の順位表における相対評価スコアを知るためには、再提出が必要である。 不正な出力や制限時間超過をした場合、提出一覧から確認できるスコアは 0 となるが、順位表には正解したテストケースに対する相対スコアの和が表示される。

実行時間について

実行時間には多少のブレが生じる。 また、システムテストでは同時に大量の実行を行うため、暫定テストに比べて数%程度実行時間が伸びる現象が確認されている。 そのため、実行時間制限ギリギリの提出がシステムテストでTLEとなる可能性がある。 プログラム内で時間を計測して処理を打ち切るか、実行時間に余裕を持たせることを推奨する。


入力

入力は以下の形式で標準入力から与えられる。

N L T
A_0 A_1 \cdots A_{L-1}
  • N はグリッドの一辺の長さを表す。
  • L は目標列の長さを表す。
  • T は操作可能な最大ターン数を表す。
  • A_kk 回目の光の発射における目標出力値を表す。

入力は以下の制約を満たす。

  • N = 20
  • L = 100
  • 300 \leq T \leq 600
  • 0 \leq A_k \leq 1

出力

まず、初期盤面を表す N 行を出力せよ。各行は長さ N の文字列であり、各文字は対応するマスの初期状態を表す 1 文字 ., /, \, Z, N, # のいずれかである。

続いて、最大で T 行の操作を出力せよ。各行には、以下のいずれかの形式で操作を出力する。

マス (i, j) に物体 c を設置する場合:

1 i j c

ここで c/, \, Z, N, # のいずれかである。この操作は、マス (i, j) が空マスである場合にのみ実行できる。

マス (i, j) に設置されている物体を除去する場合:

2 i j

この操作は、マス (i, j) が空マスでない場合にのみ実行できる。

マス (i, j) に設置されている反射板または分光板を回転する場合:

3 i j

この操作は、マス (i, j) に反射板もしくは分光板が置かれている場合にのみ実行できる。

光を発射する場合:

4

出力に含まれる光の発射操作はちょうど L 回でなければならない。

例を見る

入力生成方法

以下では、\mathrm{rand}(L, U)L 以上 U 以下の整数値を一様ランダムに生成する関数を表す。

N, L, T の生成

  • N = 20
  • L = 100
  • T = \mathrm{rand}(300, 600)

目標列 A の生成

k = 0, 1, \ldots, L-1 について、独立に以下のように生成する。

\[ A_k = \mathrm{rand}(0, 10^6) \times 10^{-6} \]

ツール(入力ジェネレータ・ビジュアライザ)

コンテスト期間中に、ビジュアライズ結果の共有や、解法・考察に関する言及は禁止されています。ご注意下さい。

生成AIの利用に関して

本コンテストで生成AIを利用する場合は、以下の内容をプロンプトもしくは各生成AIツールが読み込む指示ファイル(例:AGENTS.md、CLAUDE.md など)に設定する必要があります。

I am currently participating in an AtCoder Heuristic Contest, and I will use this generative AI as assistance for developing my solution.

When using this generative AI, the "AtCoder Heuristic Contest Generative AI Usage Rules - Version 20250616" apply.
https://info.atcoder.jp/entry/ahc-llm-rules-en

You must not perform any of the following actions:

* Run the solution program.
* Most importantly, you must not run the solution program and then automatically repeat improvements to the approach or code based on the execution results.
* Access social media or YouTube to collect information about the contest.

Here, "solution program" refers to any program created or being created for the purpose of solving this contest problem, regardless of whether it was created by the user or by generative AI, and regardless of whether it is still in progress or already complete.

Compiling the solution program, and giving advice based on execution results, logs, scores, or similar information provided by the user, are not included in the prohibited actions above.

Story

Takahashi is developing a device that automatically adjusts the brightness of stage lighting.

This device can control the path of incoming light by placing mirrors, splitters, and absorbing blocks on a grid. Mirrors change the direction of light, splitters split light into two directions, and absorbing blocks make light disappear.

You are given in advance the sequence of brightness values that will be needed. Operate the objects on the grid and emit light so as to realize a sequence of brightness values as close to the target as possible.

Problem Statement

There is an N \times N grid. Let (0, 0) denote the coordinates of the top-left cell, and let (i, j) denote the coordinates of the cell located i cells downward and j cells rightward from there.

At most one object can be placed in each cell. The objects that can be placed are as follows.

  • A mirror / connecting the lower-left and upper-right corners
  • A mirror \ connecting the upper-left and lower-right corners
  • A splitter Z connecting the lower-left and upper-right corners
  • A splitter N connecting the upper-left and lower-right corners
  • An absorbing block #

For the splitters Z and N, the direction of the diagonal stroke in the character corresponds to the direction of the splitter. A cell with no object placed in it is called an empty cell.

You may set the initial board freely. Setting the initial board does not count toward the number of operation turns.

There is a light emitter on the upper edge of cell (0, N/2). From this emitter, you can emit a light ray of intensity 1 and clarity 60 downward.

The emitted light ray travels on the board while changing its direction, intensity, and clarity as described below.

Empty cell: If a light ray enters an empty cell, it continues straight.

Absorbing block: If a light ray enters an absorbing block, that ray disappears.

Mirror: If a light ray enters a mirror, it is reflected and changes direction by 90 degrees. The direction of reflection is determined by the direction of the light ray immediately before entering the mirror and the orientation of the mirror, as shown in the following table.

Direction before entering Mirror / Mirror \

Splitter: If a light ray enters a splitter and its clarity is positive, it splits into two rays: one that continues straight, and one that proceeds in the same direction as it would if it were reflected by a mirror with the same orientation as the splitter. Each of the two resulting rays has half the intensity of the incoming ray, and its clarity is smaller by 1 than that of the incoming ray.

On the other hand, if a light ray with clarity 0 enters another splitter, the light is scattered and causes an unintended effect on the device's output. The scattered ray is no longer tracked and is not added to the output value. For one emission, the sum of the intensities of all scattered rays is called the scattering value.

Output: If a light ray exits the grid, that ray is regarded as having been output. It may exit from any side of the grid. For one emission, the sum of the intensities of all light rays that exit the grid is called the output value.

Example

example

Solid lines represent mirrors, dashed lines represent splitters, and black cells represent absorbing blocks. Of the light emitted from the top, half is directly output outside the grid, and the remaining half enters the loop in the lower right. Of the light that enters the loop, half is absorbed, 1/4 is output outside the grid, and the remaining 1/4 enters the loop again. By repeating this process, the final output value becomes \frac{2}{3} - \frac{1}{3}\cdot 2^{-59}, and the scattering value becomes 2^{-60}.

After setting the initial board, you may perform the following operations for at most T turns.

  • Place an object in an empty cell.
  • Remove an object placed in a cell.
  • Rotate one mirror or splitter. This swaps / with \, and swaps N with Z.
  • Emit light.

You are given a target sequence of light intensities A_0, A_1, \ldots, A_{L-1}. You must perform the light emission operation exactly L times.

Let B_k be the output value obtained by the k-th light emission, and let R_k be the scattering value. Your goal is to make the error between A and B, as well as the total scattering value, as small as possible.

Supplement on the Output Value and Scattering Value

For boards containing loops formed by splitters, light rays may branch and circulate indefinitely. Even in such cases, it is possible to define the output value exactly by solving a system of linear equations. However, computations using real numbers may cause the score to change due to numerical errors.

Therefore, in this problem, each light ray has a clarity value, and the number of times it can pass through splitters is limited to at most 60. If a light ray with clarity 0 enters another splitter, that ray is scattered and is no longer tracked. Thus, the process for a single emission always terminates after a finite number of steps.

Also, for computation purposes, if we regard the incoming light as having intensity 2^{60}, then each pass through a splitter only halves the intensity, so all light-ray intensities can be handled as integers. The actual output value and scattering value are obtained by dividing the resulting integer values by 2^{60}, respectively.

Sample Python Implementation for Computing the Output Value and Scattering Value
from collections import defaultdict

# Directions: 0=up, 1=right, 2=down, 3=left
DI = [-1, 0, 1, 0]
DJ = [0, 1, 0, -1]

INITIAL_CLARITY = 60
UNIT = 1 << INITIAL_CLARITY


def reflect_dir(ch, d):
    if ch in ("/", "Z"):
        return [1, 0, 3, 2][d]
    if ch in ("\\", "N"):
        return [3, 2, 1, 0][d]
    raise ValueError("not a mirror or splitter")


def calc_B_R(board):
    """
    Returns (B, R) when light is emitted once on the current board.

    B is the output value, and R is the scattering value.
    Both are returned as integer values in units of 1 / 2^60.
    """
    n = len(board)
    memo = {}

    def advance(i, j, d):
        """
        Follow empty cells and mirrors until reaching outside the grid,
        an absorbing block, or a splitter.
        """
        path = []

        while True:
            key = (i, j, d)
            if key in memo:
                res = memo[key]
                break

            path.append(key)

            ch = board[i][j]

            if ch == "#":
                res = ("absorb", None)
                break

            if ch in ("Z", "N"):
                res = ("split", (i, j, d))
                break

            if ch == ".":
                nd = d
            elif ch in ("/", "\\"):
                nd = reflect_dir(ch, d)
            else:
                raise ValueError("invalid board character")

            ni = i + DI[nd]
            nj = j + DJ[nd]

            if not (0 <= ni < n and 0 <= nj < n):
                res = ("out", None)
                break

            i, j, d = ni, nj, nd

        for key in path:
            memo[key] = res

        return res

    cur = defaultdict(int)
    cur[(0, n // 2, 2)] = UNIT

    B = 0
    R = 0

    for clarity in range(INITIAL_CLARITY, -1, -1):
        nxt = defaultdict(int)

        for (i, j, d), amount in cur.items():
            kind, arg = advance(i, j, d)

            if kind == "out":
                B += amount

            elif kind == "absorb":
                pass

            elif kind == "split":
                if clarity == 0:
                    R += amount
                    continue

                si, sj, sd = arg
                half = amount // 2

                for nd in (sd, reflect_dir(board[si][sj], sd)):
                    ni = si + DI[nd]
                    nj = sj + DJ[nd]

                    if not (0 <= ni < n and 0 <= nj < n):
                        B += half
                    else:
                        nxt[(ni, nj, nd)] += half

        cur = nxt

    return B, R

Scoring

Let B_k be the output value obtained by the k-th light emission, and let R_k be its scattering value. Define the error E as follows.

\[ E = \sum_{k=0}^{L-1} \left(|A_k - B_k| + R_k\right) \]

Then, you obtain the following absolute score.

\[ 1 + \mathrm{round}(10^9 \times E) \]

The lower the absolute score, the better.

For each test case, we compute the relative score \mathrm{round}(10^9\times \frac{\mathrm{MIN}}{\mathrm{YOUR}}), where YOUR is your absolute score and MIN is the lowest absolute score among all competitors obtained on that test case. The score of the submission is the sum of the relative scores.

The final ranking will be determined by the system test with more inputs which will be run after the contest is over. In both the provisional/system test, if your submission produces illegal output or exceeds the time limit for some test cases, only the score for those test cases will be zero, and your submission will be excluded from the MIN calculation for those test cases.

The system test will be performed only for the last submission which received a result other than CE . Be careful not to make a mistake in the final submission.

Number of test cases

  • Provisional test: 50
  • System test: 2000. We will publish seeds.txt (sha256=09a07ffd9ee0e93469394a2cb36a22a9b4be312865de69d90d27198173124f04) after the contest is over.

About relative evaluation system

In both the provisional/system test, the standings will be calculated using only the last submission which received a result other than CE. Only the last submissions are used to calculate the MIN for each test case when calculating the relative scores.

The scores shown in the standings are relative, and whenever a new submission arrives, all relative scores are recalculated. On the other hand, the score for each submission shown on the submissions page is the sum of the absolute score for each test case, and the relative scores are not shown. In order to know the relative score of submission other than the latest one in the current standings, you need to resubmit it. If your submission produces illegal output or exceeds the time limit for some test cases, the score shown on the submissions page will be 0, but the standings show the sum of the relative scores for the test cases that were answered correctly.

About execution time

Execution time may vary slightly from run to run. In addition, since system tests simultaneously perform a large number of executions, it has been observed that execution time increases by several percent compared to provisional tests. For these reasons, submissions that are very close to the time limit may result in TLE in the system test. Please measure the execution time in your program to terminate the process, or have enough margin in the execution time.


Input

Input is given from Standard Input in the following format.

N L T
A_0 A_1 \cdots A_{L-1}
  • N is the side length of the grid.
  • L is the length of the target sequence.
  • T is the maximum number of operation turns.
  • A_k is the target output value for the k-th light emission.

The input satisfies the following constraints.

  • N = 20
  • L = 100
  • 300 \leq T \leq 600
  • 0 \leq A_k \leq 1

Output

First, output N lines representing the initial board. Each line must be a string of length N, and each character must be one of ., /, \, Z, N, and #, representing the initial state of the corresponding cell.

Then, output at most T lines of operations. Each line must be in one of the following formats.

To place an object c in cell (i, j):

1 i j c

Here, c must be one of /, \, Z, N, and #. This operation can be performed only when cell (i, j) is empty.

To remove an object placed in cell (i, j):

2 i j

This operation can be performed only when cell (i, j) is not empty.

To rotate a mirror or splitter placed in cell (i, j):

3 i j

This operation can be performed only when a mirror or splitter is placed in cell (i, j). It swaps / with \, and swaps Z with N.

To emit light:

4

The output must contain exactly L light emission operations.

Show example

Input Generation

Here, \mathrm{rand}(L, U) denotes a function that generates a uniformly random integer between L and U, inclusive.

Generation of N, L, T

  • N = 20
  • L = 100
  • T = \mathrm{rand}(300, 600)

Generation of Target Sequence A

For each k = 0, 1, \ldots, L-1, generate independently as follows.

\[ A_k = \mathrm{rand}(0, 10^6) \times 10^{-6} \]

Tools (Input generator and visualizer)

Please be aware that sharing visualization results or discussing solutions/ideas during the contest is prohibited.

Regarding the Use of Generative AI

If you use generative AI in this contest, you must include the following content in your prompt or in the instruction file read by each generative AI tool, such as AGENTS.md or CLAUDE.md.

I am currently participating in an AtCoder Heuristic Contest, and I will use this generative AI as assistance for developing my solution.

When using this generative AI, the "AtCoder Heuristic Contest Generative AI Usage Rules - Version 20250616" apply.
https://info.atcoder.jp/entry/ahc-llm-rules-en

You must not perform any of the following actions:

* Run the solution program.
* Most importantly, you must not run the solution program and then automatically repeat improvements to the approach or code based on the execution results.
* Access social media or YouTube to collect information about the contest.

Here, "solution program" refers to any program created or being created for the purpose of solving this contest problem, regardless of whether it was created by the user or by generative AI, and regardless of whether it is still in progress or already complete.

Compiling the solution program, and giving advice based on execution results, logs, scores, or similar information provided by the user, are not included in the prohibited actions above.