Problem: icpc2023_asia_macau_g
Difficulty: medium
Categories: game, bit, data structures
Time limit: 1000 ms, memory limit: 1024 MB

# This is an interactive problem.

Alice and Bob are playing the following game. The game is specified by a parameter $t \in \{ 0 , 1 \}$ and is described as follows:

There are $n$ integers $a _ { 1 } , a _ { 2 } , \ldots , a _ { n } \in \{ 0 , 1 \}$ , arranged in a line. The two players take turns to perform the following operation when at least two integers are remaining, starting from Alice:

• Choose two integers $x$ and $y$ that are adjacent, replace them with either $x + y$ or $x \times y$ (arithmetic addition or arithmetic multiplication).

The game ends when there is only one integer remaining, and the result of the game is judged by the parity of the remaining integer and the parameter $t$ as follows:

• $t = 0$ : Alice wins if the remaining integer is even and Bob wins otherwise.   
• $t = 1$ : Alice wins if the remaining integer is odd and Bob wins otherwise.

You are given the initial $n$ integers $a _ { 1 } , a _ { 2 } , \ldots , a _ { n }$ and the parameter $t$ . You must choose to play as Alice or Bob, then play this game against the interactor who takes the opponent’s role. Your goal is to win the game.

# Input

The first line of input contains two integers $n$ and $t$ ( $2 \leq n \leq 5 0 0 , t \in \{ 0 , 1 \}$ ), denoting the size of the array and the parameter of the game.

The second line of input contains $n$ integers $a _ { 1 } , a _ { 2 } , \ldots , a _ { n }$ ( $0 \leq a _ { i } \leq 1$ ), denoting the array.

# Interaction Protocol

The interaction begins after reading the two integers $n , t$ and the array $\{ a _ { i } \} _ { 1 \leq i \leq n }$

You should start interaction by printing a single line containing either “Alice” or “Bob”, representing the player you choose to play as. If you choose to play Alice, you are the first to operate. Otherwise, you are the second to operate.

The game proceeds as follows:

• If it’s your turn to operate. Suppose the current array is $b _ { 1 } , b _ { 2 } , \dots , b _ { m }$ with length $m$ $2 \leq m \leq n$ ), you should output an integer $p$ ( $1 \leq p \leq m - 1 _ { \cdot }$ ) and a character $c$ $( c \in \{ { ^ { \circ } } + , { ^ { \circ } } { * } ^ { \prime } \} )$ in a line, separated by a space, denoting you choose to merge the two integers $b _ { p }$ and $b _ { p + 1 }$ with the operation denoted by $c$ . After your operation, the array becomes $b _ { 1 } , b _ { 2 } , \dotsc , b _ { p - 1 } , ( b _ { p } \ c b _ { p + 1 } ) , b _ { p + 2 } , \dotsc , b _ { m }$ with length $m - 1$ .   
• If it’s the opponent’s turn to operate. Suppose the current array is $b _ { 1 } , b _ { 2 } , \dots , b _ { m }$ with length $m$ ( $2 \leq m \leq n$ ), you should read an integer $p$ ( $0 \leq p \leq m - 1$ ) and a character $c$ $( c \in \{ { ^ { \circ } } + , { ^ { \circ } } { * } ^ { \prime } \} )$ in a line, denoting the opponent chooses to merge the two integers $b _ { p }$ and $b _ { p + 1 }$ with the operation denoted by $c$ . After the opponent’s operation, the array becomes $b _ { 1 } , b _ { 2 } , \dotsc , b _ { p - 1 } , ( b _ { p } \ c b _ { p + 1 } ) , b _ { p + 2 } , \dotsc , b _ { m }$ with length $m - 1$ . Here, if the integer $p$ is $0$ , then it means the last operation you perform is incorrect. In that case, your program should terminate immediately to receive a “Wrong Answer” verdict, or you could get any possible verdict.

When there is only one integer in the array, the game finishes and is judged according to the rule in the statement. If you win the game, your program will be considered correct for this test case. If you lose the game, or it is you to perform the last operation and your last operation is invalid, you will receive a “Wrong Answer” verdict. You should terminate the program immediately when the game finishes.

After printing the player you choose to play as and the operation you perform, do not forget to output the end of the line and flush the output. Otherwise, you may not get the correct verdict. To flush the output, use:

• fflush(stdout) or cout.flush() in C++;   
• System.out.flush() in Java;   
• stdout.flush() in Python;

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>41</td><td></td></tr><tr><td>0101</td><td></td></tr><tr><td></td><td>Alice</td></tr><tr><td></td><td></td></tr><tr><td>2*</td><td>1+</td></tr></table>

# Note

Note that additional empty lines in the sample test are for better understanding, and you should not output any additional empty lines in your program.

Here, we provide graphical illustrations of the interaction in the sample test. For the given $\{ a _ { i } \} _ { 1 \leq i \leq n }$ and $t$ , it is optimal for the player to choose to play Alice and go first.

![](images/5157783eb7691df2ae5941895ccd7e4bc2b4071573a2eeb310367e5b9525e3b3.jpg)

In the sample interaction, the player chooses to merge the first two numbers with the addition operator,

![](images/f66242b22245916fcb8e663bd932cf836d02261ca92b7b0ec4a16e7dc6b0af56.jpg)

and the opponent chooses to merge the last two numbers with the multiplication operator.

![](images/469c959e130ed6733141393c272803177f52e6b4db81c84daf415dbbf545e3b4.jpg)

Then, the player chooses to merge the remaining two numbers with the addition operator and wins the game.

![](images/28d447e6df73f0b9678036d596d4ccfb11656ced0124731dbdd427cbc13d21ad.jpg)

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

static long long queries = 0;
static long long query_limit = 0;


static void log_metrics() {
    try {
        
        
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}


static void finish(TResult verdict, const string& msg) {
    log_metrics(); 
    quitf(verdict, "%s", msg.c_str());
}



int apply_op(int x, int y, char op) {
    if (op == '+') return (x + y) % 2; 
    if (op == '*') return (x * y) % 2; 
    return -1;
}

int main(int argc, char* argv[]) {
    
    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    int n = inf.readInt();
    int t = inf.readInt();
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        a[i] = inf.readInt();
    }

    
    query_limit = n; 
    
    tout << "queries=" << queries << endl;
    tout << "query_limit=" << query_limit << endl;

    
    cout << n << " " << t << endl;
    for (int i = 0; i < n; ++i) {
        cout << a[i] << (i == n - 1 ? "" : " ");
    }
    cout << endl; 

    
    string role_token;
    if (!(cin >> role_token)) {
        finish(_pe, "Failed to read role (EOF)");
    }
    
    bool user_is_alice = false;
    if (role_token == "Alice") user_is_alice = true;
    else if (role_token == "Bob") user_is_alice = false;
    else finish(_wa, "Invalid role '" + role_token + "'. Expected 'Alice' or 'Bob'");

    
    
    
    
    int interactor_target = -1;
    if (user_is_alice) {
        
        
        interactor_target = 1 - t;
    } else {
        
        
        interactor_target = t;
    }

    
    
    for (int step = 0; step < n - 1; ++step) {
        bool is_alice_turn = (step % 2 == 0);
        bool is_user_turn = (user_is_alice == is_alice_turn);

        if (is_user_turn) {
            
            string p_str;
            if (!(cin >> p_str)) {
                finish(_pe, "Expected user move (p), got EOF");
            }
            
            int p = -1;
            try {
                size_t idx;
                p = stoi(p_str, &idx);
                if (idx != p_str.size()) finish(_pe, "Token p is not a valid integer");
            } catch (...) {
                finish(_pe, "Token p is not a valid integer");
            }

            char c;
            if (!(cin >> c)) {
                finish(_pe, "Expected user move (c), got EOF");
            }

            
            if (p < 1 || p >= (int)a.size()) {
                finish(_wa, format("Invalid index p=%d. Must be in [1, %d]", p, (int)a.size() - 1));
            }
            if (c != '+' && c != '*') {
                finish(_wa, format("Invalid operator c='%c'. Must be '+' or '*'", c));
            }

            
            int idx = p - 1; 
            int val = apply_op(a[idx], a[idx+1], c);
            
            
            a[idx] = val;
            a.erase(a.begin() + idx + 1);

            queries++; 
            
            
            if (queries % 10 == 0) log_metrics();

        } else {
            
            
            int best_idx = -1;
            char best_op = ' ';
            bool found = false;

            
            for (int i = 0; i < (int)a.size() - 1; ++i) {
                
                if (apply_op(a[i], a[i+1], '+') == interactor_target) {
                    best_idx = i;
                    best_op = '+';
                    found = true;
                    break; 
                }
                
                if (apply_op(a[i], a[i+1], '*') == interactor_target) {
                    best_idx = i;
                    best_op = '*';
                    found = true;
                    break;
                }
            }

            
            if (!found) {
                best_idx = 0;
                best_op = '+';
            }

            
            cout << (best_idx + 1) << " " << best_op << endl;

            
            int val = apply_op(a[best_idx], a[best_idx+1], best_op);
            a[best_idx] = val;
            a.erase(a.begin() + best_idx + 1);
        }
    }

    
    if (a.size() != 1) {
        finish(_wa, "Internal Error: Game finished with size != 1");
    }
    
    int final_val = a[0];
    
    
    
    
    bool alice_wins = (final_val == t);

    if (user_is_alice) {
        if (alice_wins) finish(_ok, "Contestant (Alice) wins");
        else finish(_wa, "Contestant (Alice) loses");
    } else {
        if (!alice_wins) finish(_ok, "Contestant (Bob) wins");
        else finish(_wa, "Contestant (Bob) loses");
    }

    return 0;
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;


int N_init, T_target;
vector<int> A;
long long queries = 0;
long long query_limit = 0;


void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {}
}


void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}


int apply_op(int x, int y, char op) {
    if (op == '+') return (x + y) % 2; 
    if (op == '*') return (x * y) % 2;
    return 0;
}




int solve_greedy(int c0, int c1, int turn) {
    int cur_c0 = c0;
    int cur_c1 = c1;
    
    
    while (cur_c0 + cur_c1 > 1) {
        bool is_alice = (turn % 2 == 0);
        
        int target = (is_alice ? T_target : 1 - T_target);
        bool wants_1 = (target == 1);
        
        if (wants_1) {
            
            if (cur_c0 > 0 && cur_c1 > 0) {
                
                cur_c0--;
            } else if (cur_c0 == 0) {
                
                cur_c1--;
            } else {
                
                cur_c0--;
            }
        } else {
            
            if (cur_c0 > 0 && cur_c1 > 0) {
                
                cur_c1--;
            } else if (cur_c1 == 0) {
                
                cur_c0--;
            } else {
                
                cur_c1 -= 2;
                cur_c0++;
            }
        }
        turn++;
    }
    return (cur_c1 > 0 ? 1 : 0);
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    
    atexit(log_metrics);
    
    
    N_init = inf.readInt();
    T_target = inf.readInt();
    int c0 = 0, c1 = 0;
    for (int i = 0; i < N_init; ++i) {
        int x = inf.readInt();
        A.push_back(x);
        if (x == 0) c0++; else c1++;
    }
    
    
    query_limit = N_init - 1;
    log_metrics();

    
    cout << N_init << " " << T_target << endl;
    for (int i = 0; i < N_init; ++i) {
        cout << A[i] << (i == N_init - 1 ? "" : " ");
    }
    cout << endl;
    
    
    string role_str;
    if (!(cin >> role_str)) finish(_wa, "Cannot read role");
    
    bool contestant_is_alice = false;
    if (role_str == "Alice") contestant_is_alice = true;
    else if (role_str == "Bob") contestant_is_alice = false;
    else finish(_wa, "Invalid role: " + role_str);
    
    bool interactor_is_alice = !contestant_is_alice;
    
    int moves = 0;
    while ((int)A.size() > 1) {
        
        queries = moves;
        log_metrics();
        
        bool is_alice_turn = (moves % 2 == 0);
        bool my_turn = (is_alice_turn == interactor_is_alice);
        
        if (my_turn) {
            
            int best_idx = 0;
            char best_op = '+';
            bool found_win = false;
            int my_target = (interactor_is_alice ? T_target : 1 - T_target);
            
            
            for (int i = 0; i < (int)A.size() - 1; ++i) {
                for (char op : {'+', '*'}) {
                    
                    int x = A[i];
                    int y = A[i+1];
                    int res = apply_op(x, y, op);
                    
                    int n_c0 = c0;
                    int n_c1 = c1;
                    if (x==0) n_c0--; else n_c1--;
                    if (y==0) n_c0--; else n_c1--;
                    if (res==0) n_c0++; else n_c1++;
                    
                    int outcome = solve_greedy(n_c0, n_c1, moves + 1);
                    
                    if (outcome == my_target) {
                        best_idx = i;
                        best_op = op;
                        found_win = true;
                        goto found;
                    }
                }
            }
            
            
            if (!found_win) {
                bool wants_1 = (my_target == 1);
                bool attacked = false;
                
                for (int i = 0; i < (int)A.size() - 1; ++i) {
                    if ((A[i] ^ A[i+1]) == 1) { 
                        best_idx = i;
                        best_op = (wants_1 ? '+' : '*');
                        attacked = true;
                        break;
                    }
                }
                if (!attacked) {
                    
                    best_idx = 0; 
                    if (wants_1) {
                        if (c1 >= 2) best_op = '*'; 
                        else best_op = '+';         
                    } else {
                        if (c1 >= 2) best_op = '+'; 
                        else best_op = '*';         
                    }
                }
            }
            
            found:;
            
            int i = best_idx;
            int x = A[i];
            int y = A[i+1];
            int res = apply_op(x, y, best_op);
            
            
            cout << (i + 1) << " " << best_op << endl;
            
            
            vector<int> next_A;
            next_A.reserve(A.size() - 1);
            for(int k=0; k<i; ++k) next_A.push_back(A[k]);
            next_A.push_back(res);
            for(int k=i+2; k<(int)A.size(); ++k) next_A.push_back(A[k]);
            A = next_A;
            
            
            if(x==0) c0--; else c1--;
            if(y==0) c0--; else c1--;
            if(res==0) c0++; else c1++;
            
        } else {
            
            int p; 
            string op_str;
            if (!(cin >> p >> op_str)) finish(_wa, "Cannot read move");
            
            
            if (p < 1 || p >= (int)A.size()) finish(_wa, "Invalid index: " + to_string(p));
            if (op_str.length() != 1 || (op_str[0] != '+' && op_str[0] != '*')) finish(_wa, "Invalid op: " + op_str);
            char op = op_str[0];
            
            int i = p - 1;
            int x = A[i];
            int y = A[i+1];
            int res = apply_op(x, y, op);
            
            
            vector<int> next_A;
            next_A.reserve(A.size() - 1);
            for(int k=0; k<i; ++k) next_A.push_back(A[k]);
            next_A.push_back(res);
            for(int k=i+2; k<(int)A.size(); ++k) next_A.push_back(A[k]);
            A = next_A;
            
            
            if(x==0) c0--; else c1--;
            if(y==0) c0--; else c1--;
            if(res==0) c0++; else c1++;
        }
        
        moves++;
    }
    
    
    queries = moves;
    int final_val = A[0];
    bool alice_wins = (final_val == T_target);
    
    if (alice_wins == contestant_is_alice) {
        finish(_ok, "Contestant won");
    } else {
        finish(_wa, "Contestant lost");
    }
    return 0;
}


Test generator (C++):









#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    
    string mode = opt<string>("mode", "non");
    int n_opt = opt<int>("n", -1);
    int t_opt = opt<int>("t", -1);
    
    
    int seed_idx = -1;
    try {
        seed_idx = stoi(argv[1]);
    } catch (...) {
        
    }

    int n;
    int t;
    vector<int> a;

    
    if (seed_idx >= 1 && seed_idx <= 10) {
        
        switch (seed_idx) {
            case 1: n = 2; t = 0; a = {0, 0}; break;
            case 2: n = 2; t = 1; a = {0, 0}; break;
            case 3: n = 2; t = 0; a = {0, 1}; break;
            case 4: n = 2; t = 1; a = {1, 0}; break;
            case 5: n = 3; t = 0; a = {1, 1, 1}; break;
            case 6: n = 3; t = 1; a = {0, 0, 0}; break;
            case 7: n = 3; t = 0; a = {1, 0, 1}; break;
            case 8: n = 4; t = 1; a = {0, 1, 1, 0}; break;
            case 9: n = 5; t = 0; a = {1, 0, 0, 0, 1}; break;
            case 10: n = 10; t = 1; a.assign(10, 1); break;
            default: n = 2; t = 0; a = {0, 0}; break; 
        }
        
        if (t_opt != -1) t = t_opt;
    } 
    
    else {
        
        if (n_opt != -1) n = n_opt;
        else n = rnd.next(2, 500);

        
        if (t_opt != -1) t = t_opt;
        else t = rnd.next(0, 1);

        a.resize(n);

        if (mode == "non") {
            
            
            int dist_type = rnd.next(1, 10);
            
            if (dist_type <= 5) {
                
                for (int &x : a) x = rnd.next(0, 1);
            } else if (dist_type <= 8) {
                
                int dominant = rnd.next(0, 1);
                int prob = rnd.next(60, 90);
                for (int &x : a) x = (rnd.next(0, 100) < prob) ? dominant : 1 - dominant;
            } else {
                
                int current = rnd.next(0, 1);
                for (int i = 0; i < n; ) {
                    int len = rnd.next(1, max(1, min(n - i, n / 5)));
                    for (int k = 0; k < len; k++) a[i++] = current;
                    current = 1 - current;
                }
            }
        } else {
            
            
            int pattern = rnd.next(1, 5);
            
            if (pattern == 1) {
                
                int start = rnd.next(0, 1);
                for (int i = 0; i < n; i++) a[i] = (start + i) % 2;
            } else if (pattern == 2) {
                
                int dom = rnd.next(0, 1);
                fill(a.begin(), a.end(), dom);
                int cnt = rnd.next(1, max(1, n / 10));
                vector<int> p = rnd.distinct(cnt, 0, n - 1); 
                for (int idx : p) a[idx] = 1 - dom;
            } else if (pattern == 3) {
                
                for (int i = 0; i < (n + 1) / 2; i++) {
                    a[i] = rnd.next(0, 1);
                    a[n - 1 - i] = a[i];
                }
            } else if (pattern == 4) {
                
                int val = rnd.next(0, 1);
                for (int i = 0; i < n; i++) {
                    if (i % 2 == 0) val = rnd.next(0, 1); 
                    a[i] = val;
                }
            } else {
                
                int bias = rnd.next(10, 90);
                for (int &x : a) x = (rnd.next(0, 100) < bias) ? 1 : 0;
            }
        }
    }

    
    cout << n << " " << t << "\n";
    for (int i = 0; i < n; i++) {
        cout << a[i] << (i == n - 1 ? "" : " ");
    }
    cout << "\n";

    return 0;
}