公式

A - 暗号化リレー / Encryption Relay 解説 by admin

Gemini 3.0 Flash (Thinking)

概要

\(N\) 台のサーバーを順に経由しながら、各サーバーの持つ暗号化キー \(A_i\) とデータの排他的論理和(XOR)をとっていく問題です。ただし、特定の条件(サンドイッチ検知)を満たすサーバーのキーは \(0\) として扱われます。

考察

XOR演算の性質

この問題の基本は、初期値 \(X\) に対して各サーバーの「有効なキー」を次々と XOR していくことです。XOR 演算には「どの順番で計算しても結果が変わらない(結合法則・交換法則)」という性質があるため、最終的な出力は以下の式で表せます。 $\(\text{Output} = X \oplus (\text{サーバー1の有効なキー}) \oplus (\text{サーバー2の有効なキー}) \oplus \dots \oplus (\text{サーバー} N \text{の有効なキー})\)$

サンドイッチ検知の条件

サーバー \(i\) のキーが \(0\) に書き換えられる条件は、その前後(\(i-1\) 番目と \(i+1\) 番目)のサーバーのキーと比較して以下の通りになる場合です。 - \(A_{i-1} = A_{i+1}\) かつ \(A_{i-1} \neq A_i\)

この条件を判定できるのは、前後にサーバーが存在する「中間のサーバー(\(2\) 番目から \(N-1\) 番目)」のみです。 - サーバー \(1\) とサーバー \(N\): 前後どちらかのサーバーが存在しないため、条件を満たすことはなく、常に元の \(A_1, A_N\) が使われます。 - サーバー \(i\) (\(1 < i < N\)): \(A_{i-1}\)\(A_{i+1}\) を確認して条件を判定します。

効率的な処理

\(N\) が最大 \(2 \times 10^5\) と大きいため、各サーバーについて \(1\) 回ずつ判定を行う \(O(N)\) のアルゴリズムで解く必要があります。Python では for ループで回すほか、zip やジェネレータ式を使うことで、スライス操作による無駄なメモリ消費を抑えつつ高速に処理できます。

アルゴリズム

  1. 入力 \(N, X\) と暗号化キーの配列 \(A\) を受け取る。
  2. 最終的な XOR 和を保持する変数(初期値 \(X\))を用意する。
  3. サーバー \(1\) のキー \(A_1\) を XOR する。
  4. \(i = 2\) から \(N-1\) までの各サーバーについて:
    • \(A_{i-1} = A_{i+1}\) かつ \(A_{i-1} \neq A_i\) ならば、何もしない(\(0\) を XOR するのと同等)。
    • そうでなければ、 \(A_i\) を XOR する。
  5. \(N > 1\) の場合、サーバー \(N\) のキー \(A_N\) を XOR する。
  6. 計算された値を最終的な出力とする。

計算量

  • 時間計算量: \(O(N)\)
    • サーバーの配列を \(1\) 度走査するだけで判定と XOR 計算が完了するため、 \(N\) に比例した時間で計算できます。
  • 空間計算量: \(O(N)\)
    • 入力された \(N\) 個のキーをリストとして保持するために \(O(N)\) のメモリを使用します。

実装のポイント

  • 境界条件: \(N=1\) の場合、中間のサーバーが存在しません。このとき、サーバー \(1\) (かつ最後のサーバー)である \(A_1\) のみを処理するように注意します。

  • 高速な入出力: \(N\) が大きいため、sys.stdin.read().split() などを用いて一括で入力を取得すると実行時間を短縮できます。

  • イテレータの活用: zip(a, a[1:], a[2:]) のような形式で「前・現在・次」の要素を同時に取得すると、インデックス管理の手間が省け、コードが簡潔になります。

    ソースコード

import sys
from functools import reduce
from operator import xor
from itertools import islice, chain

def solve():
    # Read all inputs efficiently from the standard input buffer
    tokens = sys.stdin.buffer.read().split()
    if not tokens:
        return
    
    # Use an iterator to parse the tokens: N (台数), X (データ), then encryption keys A
    it = map(int, tokens)
    try:
        n = next(it)
        x = next(it)
        a = list(it)
    except StopIteration:
        return

    # Security rule: server i+1 is "abnormal" if A[i] == A[i+2] and A[i] != A[i+1].
    # In 0-based indexing, server index i is abnormal if A[i-1] == A[i+1] and A[i-1] != A[i].
    # This only applies to middle servers (indices 1 to N-2).
    
    # We use a generator expression with zip and islice to evaluate effective keys.
    # zip(a, islice(a, 1, None), islice(a, 2, None)) yields (A[i-1], A[i], A[i+1]) for i in 1..N-2.
    middle_effective_keys = (
        curr if not (prev == nxt and prev != curr) else 0
        for prev, curr, nxt in zip(a, islice(a, 1, None), islice(a, 2, None))
    )
    
    # Assemble the final sequence of values to be XORed:
    # 1. The original data X
    # 2. The key of the first server (A[0]), which is never abnormal
    # 3. The effective keys of all middle servers (from the generator)
    # 4. The key of the last server (A[N-1]), which is never abnormal (only if N > 1)
    final_stream = chain(
        [x, a[0]],
        middle_effective_keys,
        [a[-1]] if n > 1 else []
    )
    
    # The result is the cumulative XOR sum of the entire stream.
    # reduce(xor, ...) applies the XOR operator across all elements.
    print(reduce(xor, final_stream))

if __name__ == '__main__':
    solve()

この解説は gemini-3-flash-thinking によって生成されました。

投稿日時:
最終更新: