Official

D - Magician Editorial by evima


Step 1

Let’s think in terms of parentheses sequences. If we interpret the character 0 as ( and 1 as ), then no matter what the values of \((a_i, b_i)\) are, Alice can always make the final string on the blackboard a valid parentheses sequence. This is because at each turn she can simply choose the larger of \(a_i\) and \(b_i\) and set that position to 1.

Likewise, rather than aiming to make the string valid when read in the natural order 1, 2, 3, 4, 5, 6, one could aim to make it valid when read in some other order—say, 3, 4, 1, 6, 5, 2.

Here, there are \(\binom{2N}{N}\) strings with \(N\) zeros and \(N\) ones, but only \(\binom{2N}{N}/(N+1)\) valid parentheses sequences of length \(2N\). Thus, if Alice’s strategy is chosen appropriately (as in the example below), it might appear possible for her to succeed even if the host had \(N+1\) possible letters to declare. Certainly, with only \(N\) letters it seems even more plausible.

Example strategy (illustration)

  • If the host declares A, aim for a valid parentheses sequence when reading positions 1, 2, 3, 4, 5, 6 in this order.
  • If the host declares B, aim for a valid parentheses sequence when reading positions 3, 4, 1, 6, 5, 2 in this order.
  • If the host declares C, aim for a valid parentheses sequence when reading positions 5, 3, 6, 1, 2, 4 in this order.

However, such a strategy only works if no valid parentheses sequence “belongs” to multiple letters. For example, with the above, the sequence ()(()) (string 010011) could result under both A and B, so it fails. Is there, then, a strategy that actually works?



Step 2

As an example, consider the case \(N=4\). In fact, the following strategy succeeds:

  • If the host declares A, aim for a valid parentheses sequence when reading positions 2, 3, 4, 5, 6, 7, 8, 1 in this order.
  • If the host declares B, aim for a valid parentheses sequence when reading positions 4, 5, 6, 7, 8, 1, 3, 2 in this order.
  • If the host declares C, aim for a valid parentheses sequence when reading positions 6, 7, 8, 1, 5, 2, 3, 4 in this order.
  • If the host declares D, aim for a valid parentheses sequence when reading positions 8, 1, 7, 2, 3, 4, 5, 6 in this order.

Why does this work? Consider the depth diagram of a parentheses sequence (see Problem B’s editorial for the definition). Let \(u_l\) be the first position where the depth attains its minimum value, and \(u_r\) the last such position. For example, for )()( we have \((u_l,u_r)=(1,3)\). Then, the following holds, so no sequence belongs to multiple letters, that is, the strategy works. Note that \(u_l\) and \(u_r\) have the same parity.

  • The sequence can be produced under A if and only if \(u_l = 1\).
  • The sequence can be produced under B if and only if \(u_l = 3\) or \(u_r = 2\).
  • The sequence can be produced under C if and only if \(u_l = 5\) or \(u_r = 4\).
  • The sequence can be produced under D if and only if \(u_l = 7\) or \(u_r = 6\).

In the general case, if the host declares the \(x\)‑th uppercase letter \((1 \le x \le N)\), then reading positions \(2x,\,2x+1,\,\dots,\,N,\,1,\,2x-1,\,2,\,3,\,\dots,\,2x-2\) in this order similarly guarantees success. Therefore, the following implementation achieves AC.



Sample implementation (C++)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Order {
    int ord[20];
};

int main() {
    // step 1. Read initial input
    int N, T; cin >> N >> T;

    // step 2. Decide the order in which to read the parentheses sequence
    vector<Order> read_order(N);
    for (int num = 1; num <= N; num++) {
        Order order;
        for (int i = 0; i < 2 * N; i++) {
            if (i <= 2 * (N - num)) order.ord[i] = 2 * num + i;
            else if (i == 2 * (N - num) + 1) order.ord[i] = 1;
            else if (i == 2 * (N - num) + 2) order.ord[i] = 2 * num - 1;
            else order.ord[i] = i - (2 * (N - num) + 1);
        }
        read_order[num - 1] = order;
    }

    // step 3. Sort the \tbinom{2N}{N} cards in lexicographic order
    vector<string> card_sorted;
    for (int i = 0; i < (1 << (2 * N)); i++) {
        string str = "";
        int count_zero = 0; // record the number of '0's on the card (must be N)
        for (int j = 0; j < 2 * N; j++) {
            str += ('0' + ((i >> j) & 1));
            if (str[str.size() - 1] == '0') count_zero += 1;
        }
        if (count_zero == N) card_sorted.push_back(str);
    }
    sort(card_sorted.begin(), card_sorted.end());

    // step 4. First output
    for (string card : card_sorted) {
        int index = -1; // the reading-order index for which this card forms a valid parentheses sequence
        for (int num = 0; num < N; num++) {
            bool valid_order = true;
            int depth = 0;
            for (int i = 0; i < 2 * N; i++) {
                depth += (card[read_order[num].ord[i] - 1] == '0' ? +1 : -1);
                if (depth < 0) valid_order = false;
            }
            if (valid_order) index = num;
        }
        if (index == -1) cout << 'A';
        else cout << (char)('A' + index);
    }
    cout << endl;

    // step 5. Begin the magic
    for (int i = 0; i < T; i++) {
        string s; cin >> s;
        vector<int> inverse_order(2 * N + 1, 0);
        for (int j = 0; j < 2 * N; j++) {
            inverse_order[read_order[s[0] - 'A'].ord[j]] = j;
        }
        for (int j = 0; j < N; j++) {
            int a, b; cin >> a >> b;
            cout << (inverse_order[a] > inverse_order[b] ? a : b) << endl;
        }
    }
    return 0;
}

posted:
last update: