AtCoder Beginner Contest 126 F - XOR Matching

F - XOR Matching

問題

以下の条件を満たす長さ $2^{M+1}$ の数列 $\{a_i\}$ を、存在するならば 1 つ構成せよ。

  • $0, 1, \cdots, 2^M - 1$ がそれぞれ丁度 2 回ずつ現れる。
  • $a_i = a_j$ なる任意の $i, j \, (i \lt j)$ について、 $a_i \oplus a_{i + 1} \oplus \cdots \oplus a_j = K$ 。

ここで $\oplus$ は bitwise xor を表す。

制約

  • $0 \leq M \leq 17$
  • $0 \leq K \leq 10^9$

考察

これといって良いアイデアが浮かばなかったので、とりあえず以下のコードで実験することにした。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int main() {
    int M;
    cin >> M;

    int S = 1 << M;
    vector<int> A(S * 2);  // {0, 0, 1, 1, ..., 2^M-1, 2^M-1}
    for (int i = 0; i < S; ++i) {
        A[i * 2] = A[i * 2 + 1] = i;
    }

    // next_permutationで全パターンを調べる
    do {
        vector<int> acc(S * 2 + 1, 0);  // 累積xor
        for (int i = 0; i < S * 2; ++i) {
            acc[i + 1] = acc[i] ^ A[i];
        }

        // 上の式で区間xorを計算
        set<int> B;
        for (int i = 0; i < S * 2; ++i) {
            for (int j = i + 1; j < S * 2; ++j) {
                if (A[i] == A[j]) B.insert(acc[i] ^ acc[j] ^ A[i]);
            }
        }

        // 区間xorの値が1種類なら条件成立
        if (B.size() == 1) {
            cout << *B.begin() << ":" << A << endl;
            // setの出力はテンプレでwrapしてある
        }
    } while (next_permutation(A.begin(), A.end()));

    return 0;
}

$M = 2, K = 1$ の解を求めると以下の通り。

1
2
3
4
5
6
...
0:[0,0,3,3,2,2,1,1,]
1:[0,1,0,2,3,1,3,2,]
1:[0,1,0,3,2,1,2,3,]
0:[0,1,1,0,2,2,3,3,]
...

1:[0,1,0,3,2,1,2,3,] がとても好例で、ここから $a, b, c, \cdots, c, b, a$ のように配置をすると $a_i = a, b, c$ の場合に値が一致することに気づく。この $\cdots$ の部分が $K$ になるようにすればいい。というか $K$ 単体をそこに置けばいい。

これで $a_i \neq K$ なる $i$ については条件が満たされたが、 $a_i = K$ ではどうだろうか。何とも都合のいいことに、 $M \geq 2$ では

$$ 0 \oplus 1 \oplus \cdots K - 1 \oplus K + 1 \oplus \cdots \oplus 2^M - 1 = K $$

が成立する。これは $i \equiv 3 \pmod{4}$ で $0 \oplus 1 \oplus \cdots \oplus i = 0$ となることから従う。 よって $M \geq 2$ かつ $K \lt 2^M$ では、

$$ \{0, 1, \cdots, K - 1, K + 1, \cdots, 2^M - 1, K, \\ 2^M - 1, \cdots, K + 1, K - 1, \cdots, 1, 0, K\} $$

が解となる。 $K \geq 2^M$ ではそもそも $K$ が作れないので解なし。

そしてコーナーケースである $M \leq 1$ 。 $M = 0$ では $K = 0$ のみ解あり ($\{0, 0\}$) で、 $M = 1$ ではサンプルに答えがある。

実装例

提出 #5484245 - AtCoder Beginner Contest 126

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

using namespace std;

int main() {
    int M, K;
    cin >> M >> K;

    // コーナーケース
    // M=0は下とまとめて良い
    if (M == 1) {
        if (K >= 1) {
            cout << -1 << endl;
        } else {
            cout << "0 0 1 1" << endl;
        }
        return 0;
    }

    // 可能性判定
    if (K >= (1 << M)) {
        cout << -1 << endl;
        return 0;
    }

    // 前半の出力
    for (int i = 0; i < (1 << M); ++i) {
        if (i != K) {
            cout << i << " ";
        }
    }
    cout << K << " ";

    // 後半の出力(逆順にするだけ)
    for (int i = (1 << M) - 1; i >= 0; --i) {
        if (i != K) {
            cout << i << " ";
        }
    }
    cout << K << " " << endl;

    return 0;
}
Built with Hugo
テーマ StackJimmy によって設計されています。