Official

F - 出会いと別れ / Encounter and Farewell Editorial by en_translator


First, let us only consider whether it is possible or not. Let \(B\) be the set of integers between \(1\) (inclusive) and \(N\) (exclusive) that are not included in \(A\)
This problem is equivalent to “given a graph of \(N\) vertices with edges between \((a, b)\) such that \((a\ \mathrm{xor}\ b) \in B\), is it connected?”
If the graph is connected, then for every \(x\ (0 ≤ x < N)\) we can change \(0\) to \(x\) by repeating applying XOR with an element of \(B\).
Therefore, the problem can be boiled down to “is it true that every \(x\ (0 ≤ x < N)\) can be represented as the XOR of some elements of \(B\)?”

In terms of linear algebra, the predicate is true if and only if the \(\rm rank\) of matrix \(X = \begin{pmatrix} b_{1,1} & \cdots & b_{1,n} \\ \vdots & \ddots & \vdots \\ b_{|B|,1} & \cdots & b_{|B|,n} \end{pmatrix}\) on \(\mathbb Z/2\mathbb Z\) is equal to \(n\), where \(b_{i,j}\) denotes the \(2^j\)’s place of \(i\)-th element of \(B\)’s binary n The \(\rm rank\) of \(X\) can be found in a total of \(O(n^2|B|)\) time by means of row reduction (or in \(O(n|B|)\) time assuming \(n \le{}\) (word size)), so it can be checked fast enough.

Now let us consider how to construct the answer.
All we need is nothing else but \(n\) linearly independent row vectors from \(X\) (that is, \(n\) appropriate elements from \(B\)).
The position of linearly independent row vectors has already been found in the course of row reduction.
Once we found \(n\) linearly independent row vectors, one can construct a spanning tree by choosing an appropriate spanning tree from the \(Nn/2\) edges, or by connecting the edges in the order of Gray code.

For those who do not know linear algebra well

Let \(B = \{b_1, b_2, \dots, b_m\}\) be the set of integers between \(1\) (inclusive) and \(N\) (exclusive) that are not included in \(A\).
Let us think about the integers that can be represented as the XOR of some elements of \(B\).

If \(B = \{2^0, 2^1, \dots, 2^{n-1}\}\), then one can compose all integers from \(0\) through \(N-1\).
It can also be accomplished if the most significant bits of \(b_1, b_2, \dots, b_n\) are pairwise distinct.
We will aim at transforming into this form.

Even if we apply XOR to some element of \(B\) so that \(B = \{b_1\text{ xor }b_i, b_2, \dots, b_m\}\ (i ≠ 1)\), the resulting set of integers that can be generated is invariant.
Due to this fact, if there are two elements with the same positions of the most significant bits, one can apply XOR to one of them so that it has a lower most significant bit. Removing the elements which became \(0\) in \(B\), if it finally became a set of \(n\) integers with pairwise distinct most significant bits, then all the numbers can be generated.
The ultimate size of \(B\) is the \(\rm rank\) of \(X\). In general \(2^{\text{rank}(X)}\) integers can be generated.
All that left is use the original \(b_i\) of those elements that remained without becoming \(0\), so that we can obtain a spanning tree.

Sample Code (C++)

#include <iostream>
#include <vector>
using namespace std;
void chmin(int& a, int b){ if(a > b) a = b; }

int main(){
    int N, M;
    cin >> N >> M;
    vector ok(N, true);
    while(M--){
        int A;
        cin >> A;
        ok[A] = false;
    }
    vector<int> base, elim;
    for(int x = 1; x < N; x++) if(ok[x]){
        int y = x;
        for(int b : elim) chmin(y, y ^ b);
        if(y){
            base.push_back(x);
            elim.push_back(y);
        }
    }
    if(base.size() != __lg(N)) return puts("-1") & 0;
    int XOR = 0;
    for(int x = 1; x < N; x++){
        cout << XOR << ' ';
        XOR ^= base[__lg(x & -x)];
        cout << XOR << '\n';
    }
}

Sample Code (Python)

N, M = map(int, input().split())
ok = [True] * N
for A in map(int, input().split()):
    ok[A] = False
base = []
elim = []
for x in range(1, N):
    if not ok[x]:
        continue
    y = x
    for b in elim:
        y = min(y, y ^ b)
    if y:
        base.append(x)
        elim.append(y)

def lg(x):
    return x.bit_length() - 1

if len(base) != lg(N):
    exit(print(-1))

xor = 0
for x in range(1, N):
    print(xor, end=' ')
    xor ^= base[lg(x & -x)]
    print(xor)

Solution by beet (C++)

posted:
last update: