Problem: icpc2024_gcpc_e
Difficulty: easy
Categories: game, math
Time limit: 4000 ms, memory limit: 256 MB

As every Saturday, Eve is paying her local game club a visit. Today, Bob has brought a new game he has designed on his own. After thorough investigation, you claim that there exists a dominant strategy, so one player can always win. Bob is hesitant to accept that his game is boring to play, so he challenges you to play a round! Since you claim that there exists a dominant strategy, you may determine who begins the game.

![](images/6920571f0ae627b7ba9617f5be2e3e062a06b55a04aaae9ec604a876056045a5.jpg)  
Figure E.1: The cards in the two sample interactions.

The rules are as follows. There are $n$ cards on the table, each containing a maths operation $( +$ or $\star ^ { \dagger }$ ) and an integer, as shown in Figure E.1. Eve and Bob alternate picking cards to manipulate a common number using one of the cards. For instance, if the current value of the number is 5, and the card says $+ 3$ , then the value gets updated to 8. Each card can only be used once, and the game ends when all the cards have been used. The player who makes the first move wins if the final number is odd, and the other player wins if the final number is even. Given the list of cards and the starting value, help Eve win the game.

# Interaction

The interactor begins by outputting the starting state of the game in the following format:

• One line with an integer $n$ $1 \leq n \leq 3 0 0 )$ , the number of cards.   
• $n$ lines, each with a character $o$ and an integer $x$ $\zeta _ { O } \in \{ \cdot _ { + } , \cdot _ { \star } , \}$ , $1 \leq x \leq 1 0 ^ { 6 }$ ) indicating one of the cards.   
• One line with an integer $x$ $( 1 \leq x \leq 1 0 ^ { 6 } )$ ), the starting value.

It is guaranteed that the $n$ cards in the input are distinct.

Then, your submission must output one of “me” or “you”, depending on if you want to go first or you want the interactor to go first.

Then your submission and the interactor alternate outputting cards in the same format as above. No card can be played more than once. Your submission must exit when all cards have been played, regardless of which player made the last move.

Your submission will be accepted if it follows all the rules above and the final value has the parity indicated on the first line of your output.

Make sure you flush the buffer after each write.

A testing tool is provided to help you develop your solution.

![](images/c2b2375e15f0f3183f4d2080ade82ec7b84cd0bb54ca7a6ed7b53f8ec77fcd26.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());
}







struct Card {
    char op;
    int val;
    int type;
};

int get_type(char op, int val) {
    if (op == '+') {
        
        return (val % 2 != 0) ? 1 : 0;
    } else {
        
        return (val % 2 == 0) ? 2 : 0;
    }
}

int N;
int start_val;
int current_parity;
vector<Card> deck_z, deck_f, deck_i;
int initial_total;






int8_t memo[305][305][305];

int solve(int nz, int nf, int ni) {
    if (memo[nz][nf][ni] != -1) return memo[nz][nf][ni];

    
    
    
    int played = initial_total - (nz + nf + ni);
    int turn = played % 2; 

    vector<int> outcomes;
    
    
    if (nz > 0) {
        if (nz == 1) {
            
            
            
            
            outcomes.push_back(nf % 2);
        } else {
            
            outcomes.push_back(solve(nz - 1, nf, ni));
        }
    }

    
    if (nf > 0) {
        outcomes.push_back(solve(nz, nf - 1, ni));
    }

    
    if (ni > 0) {
        outcomes.push_back(solve(nz, nf, ni - 1));
    }

    int res;
    if (turn == 0) { 
        
        bool can_force_odd = false;
        for (int x : outcomes) if (x == 1) can_force_odd = true;
        res = can_force_odd ? 1 : 0;
    } else { 
        
        bool can_force_even = false;
        for (int x : outcomes) if (x == 0) can_force_even = true;
        res = can_force_even ? 0 : 1;
    }
    
    return memo[nz][nf][ni] = res;
}

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

    
    N = inf.readInt(1, 300, "n");
    query_limit = N;
    initial_total = N;

    vector<Card> initial_cards;
    for (int i = 0; i < N; i++) {
        string ops = inf.readToken();
        int v = inf.readInt();
        char op = ops[0];
        int type = get_type(op, v);
        Card c = {op, v, type};
        initial_cards.push_back(c);
        
        if (type == 2) deck_z.push_back(c);
        else if (type == 1) deck_f.push_back(c);
        else deck_i.push_back(c);
    }
    start_val = inf.readInt();
    current_parity = start_val % 2;

    
    cout << N << endl;
    for (const auto& c : initial_cards) {
        cout << c.op << " " << c.val << endl;
    }
    cout << start_val << endl;
    cout.flush();

    
    
    
    string role;
    if (!(cin >> role)) finish(_wa, "Unexpected EOF reading role");
    
    
    if (role == "?") cin >> role;

    bool contestant_is_p1;
    if (role == "me") contestant_is_p1 = true;
    else if (role == "you") contestant_is_p1 = false;
    else finish(_pe, "Expected 'me' or 'you', got: " + role);

    
    memset(memo, -1, sizeof(memo));

    
    for (int k = 0; k < N; k++) {
        
        
        bool is_p1_turn = (k % 2 == 0);
        bool is_contestant_turn = (is_p1_turn == contestant_is_p1);

        if (is_contestant_turn) {
            queries++; 
            
            string op_str;
            if (!(cin >> op_str)) finish(_wa, "Unexpected EOF waiting for move");
            
            
            if (op_str == "!") finish(_pe, "Unexpected '!' during game");
            if (op_str == "?") {
                if (!(cin >> op_str)) finish(_wa, "Unexpected EOF after '?'");
            }

            if (op_str.size() != 1 || (op_str[0] != '+' && op_str[0] != '*')) {
                finish(_pe, "Invalid operator token: " + op_str);
            }
            char op = op_str[0];
            int val;
            if (!(cin >> val)) finish(_wa, "Unexpected EOF reading value");

            
            int type = get_type(op, val);
            vector<Card>* deck = nullptr;
            if (type == 2) deck = &deck_z;
            else if (type == 1) deck = &deck_f;
            else deck = &deck_i;

            int found_idx = -1;
            for (int i = 0; i < (int)deck->size(); i++) {
                if ((*deck)[i].op == op && (*deck)[i].val == val) {
                    found_idx = i;
                    break;
                }
            }

            if (found_idx == -1) {
                finish(_wa, "Card not available or already played: " + string(1, op) + " " + to_string(val));
            }

            
            if (type == 1) current_parity = (current_parity + 1) % 2;
            else if (type == 2) current_parity = 0;
            

            deck->erase(deck->begin() + found_idx);

        } else {
            
            Card pick;
            bool picked = false;

            
            
            if (deck_z.empty()) {
                if (!deck_f.empty()) { pick = deck_f[0]; deck_f.erase(deck_f.begin()); }
                else if (!deck_i.empty()) { pick = deck_i[0]; deck_i.erase(deck_i.begin()); }
                else {  }
                picked = true;
            } else {
                
                int nz = deck_z.size();
                int nf = deck_f.size();
                int ni = deck_i.size();
                int want = is_p1_turn ? 1 : 0; 

                
                if (!picked && nz > 0) {
                    int outcome = (nz == 1) ? (nf % 2) : solve(nz - 1, nf, ni);
                    if (outcome == want) {
                        pick = deck_z[0]; deck_z.erase(deck_z.begin());
                        picked = true;
                    }
                }
                
                if (!picked && nf > 0) {
                    int outcome = solve(nz, nf - 1, ni);
                    if (outcome == want) {
                        pick = deck_f[0]; deck_f.erase(deck_f.begin());
                        picked = true;
                    }
                }
                
                if (!picked && ni > 0) {
                    int outcome = solve(nz, nf, ni - 1);
                    if (outcome == want) {
                        pick = deck_i[0]; deck_i.erase(deck_i.begin());
                        picked = true;
                    }
                }

                
                if (!picked) {
                    if (!deck_z.empty()) { pick = deck_z[0]; deck_z.erase(deck_z.begin()); }
                    else if (!deck_f.empty()) { pick = deck_f[0]; deck_f.erase(deck_f.begin()); }
                    else { pick = deck_i[0]; deck_i.erase(deck_i.begin()); }
                }
            }
            
            
            if (pick.type == 1) current_parity = (current_parity + 1) % 2;
            else if (pick.type == 2) current_parity = 0;

            cout << pick.op << " " << pick.val << endl;
            cout.flush();
        }

        
        if ((k + 1) % 10 == 0) log_metrics();
    }

    
    
    bool p1_wins = (current_parity == 1);
    
    bool contestant_wins = (contestant_is_p1 && p1_wins) || (!contestant_is_p1 && !p1_wins);

    if (contestant_wins) {
        finish(_ok, "Contestant won the game.");
    } else {
        finish(_wa, "Contestant lost the game.");
    }

    return 0;
}


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

using namespace std;




static long long queries = 0;
static 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());
}





struct Card {
    char op;
    int val;
    int id; 
    
    
    
    
    
    int type() const {
        if (op == '*') {
            if (val % 2 == 0) return 2; 
            else return 0;              
        } else { 
            if (val % 2 != 0) return 1; 
            else return 0;              
        }
    }
    
    string toString() const {
        return string(1, op) + " " + to_string(val);
    }
};

int N_cards;
int Start_Val;
vector<Card> all_cards;
vector<Card*> setters, flippers, identities;






int memo[301][301][301][2];



int get_goal(int p_idx) {
    return (p_idx == 0) ? 1 : 0;
}


int solve(int k, int m, int z, int p) {
    
    
    
    
    
    
    
    
    
    
    
    
    if (k == 0) return 0; 

    if (memo[k][m][z][p] != -1) return memo[k][m][z][p];
    
    int my_goal = get_goal(p);
    bool can_force_win = false;
    
    
    {
        if (k == 1) {
            
            
            int outcome = m % 2;
            if (outcome == my_goal) can_force_win = true;
        } else {
            
            
            
            if (!solve(k - 1, m, z, 1 - p)) can_force_win = true;
        }
    }
    
    
    if (!can_force_win && m > 0) {
        if (!solve(k, m - 1, z, 1 - p)) can_force_win = true;
    }
    
    
    if (!can_force_win && z > 0) {
        if (!solve(k, m, z - 1, 1 - p)) can_force_win = true;
    }
    
    return memo[k][m][z][p] = (can_force_win ? 1 : 0);
}



int get_best_move(int k, int m, int z, int p) {
    
    
    
    bool setter_wins = false;
    if (k == 1) {
        if ((m % 2) == get_goal(p)) setter_wins = true;
    } else if (k > 1) {
        if (!solve(k - 1, m, z, 1 - p)) setter_wins = true;
    }
    if (setter_wins) return 2;
    
    
    if (m > 0) {
        if (!solve(k, m - 1, z, 1 - p)) return 1;
    }
    
    
    if (z > 0) {
        if (!solve(k, m, z - 1, 1 - p)) return 0;
    }
    
    
    
    
    if (k > 0) return 2;
    if (m > 0) return 1;
    return 0;
}






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

    
    N_cards = inf.readInt();
    query_limit = N_cards;
    
    for (int i = 0; i < N_cards; i++) {
        string token = inf.readToken();
        int val = inf.readInt();
        char op = token[0];
        all_cards.push_back({op, val, i});
    }
    Start_Val = inf.readInt();
    
    
    cout << N_cards << endl;
    for (const auto& c : all_cards) {
        cout << c.toString() << endl;
    }
    cout << Start_Val << endl;
    cout.flush();
    
    log_metrics(); 
    
    
    for (int i = 0; i < N_cards; i++) {
        int t = all_cards[i].type();
        if (t == 2) setters.push_back(&all_cards[i]);
        else if (t == 1) flippers.push_back(&all_cards[i]);
        else identities.push_back(&all_cards[i]);
    }
    
    int K = setters.size();
    int M = flippers.size();
    int Z = identities.size();
    
    
    string side;
    if (!(cin >> side)) finish(_pe, "No side output");
    
    
    
    
    int my_role = -1; 
    int con_role = -1;
    
    if (side == "me") {
        
        con_role = 0;
        my_role = 1;
    } else if (side == "you") {
        
        con_role = 1;
        my_role = 0;
    } else {
        finish(_pe, "Invalid side: " + side);
    }
    
    
    memset(memo, -1, sizeof(memo));
    
    
    vector<bool> used(N_cards, false);
    int turn = 0; 
    int current_parity = (Start_Val % 2);
    
    for (int move_num = 0; move_num < N_cards; move_num++) {
        queries = move_num + 1;
        Card* pick = nullptr;
        
        if (turn == my_role) {
            
            if (K == 0) {
                
                if (!setters.empty()) { pick = setters.back(); setters.pop_back(); }
                else if (!flippers.empty()) { pick = flippers.back(); flippers.pop_back(); }
                else { pick = identities.back(); identities.pop_back(); }
            } else {
                
                int type = get_best_move(K, M, Z, my_role);
                if (type == 2) { pick = setters.back(); setters.pop_back(); K--; }
                else if (type == 1) { pick = flippers.back(); flippers.pop_back(); M--; }
                else { pick = identities.back(); identities.pop_back(); Z--; }
            }
            
            
            if (!pick) finish(_pe, "Interactor internal error: no card to play");
            
            used[pick->id] = true;
            cout << pick->toString() << endl;
            
            
            if (pick->op == '+') current_parity = (current_parity + pick->val) % 2;
            else current_parity = (current_parity * pick->val) % 2;
            
        } else {
            
            string op_str;
            if (!(cin >> op_str)) finish(_pe, "Unexpected EOF waiting for card op");
            int val;
            if (!(cin >> val)) finish(_pe, "Unexpected EOF waiting for card val");
            
            char op = op_str[0];
            
            
            int found_idx = -1;
            for (int i = 0; i < N_cards; i++) {
                if (!used[i] && all_cards[i].op == op && all_cards[i].val == val) {
                    found_idx = i;
                    break;
                }
            }
            
            if (found_idx == -1) {
                finish(_wa, "Played invalid or used card: " + op_str + " " + to_string(val));
            }
            
            used[found_idx] = true;
            pick = &all_cards[found_idx];
            
            
            int t = pick->type();
            auto remove_from = [&](vector<Card*>& vec) {
                for (auto it = vec.begin(); it != vec.end(); ++it) {
                    if ((*it)->id == found_idx) {
                        vec.erase(it);
                        return;
                    }
                }
            };
            
            if (t == 2) { K--; remove_from(setters); }
            else if (t == 1) { M--; remove_from(flippers); }
            else { Z--; remove_from(identities); }
            
            
            if (pick->op == '+') current_parity = (current_parity + pick->val) % 2;
            else current_parity = (current_parity * pick->val) % 2;
        }
        
        turn = 1 - turn;
    }
    
    
    int needed_parity = get_goal(con_role);
    if (current_parity == needed_parity) {
        finish(_ok, "Contestant won (Parity " + to_string(current_parity) + ")");
    } else {
        finish(_wa, "Contestant lost (Parity " + to_string(current_parity) + ", expected " + to_string(needed_parity) + ")");
    }
    
    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 seed_idx = atoi(argv[1]);

    int n;
    int start_val;
    vector<pair<char, int>> cards;
    set<pair<char, int>> distinct_check;

    
    auto try_add = [&](char op, int val) {
        if (distinct_check.count({op, val})) return false;
        distinct_check.insert({op, val});
        cards.push_back({op, val});
        return true;
    };

    if (seed_idx == 1) {
        
        n = 1;
        start_val = 1;
        try_add('+', 1); 
    } else if (seed_idx == 2) {
        
        
        n = 300;
        start_val = 2;
        for (int i = 0; i < n; i++) {
            while(!try_add('+', rnd.next(1, 500000) * 2));
        }
    } else if (seed_idx == 3) {
        
        
        n = 300;
        start_val = 3; 
        for (int i = 0; i < n; i++) {
            
            while(!try_add('*', rnd.next(1, 500000) * 2));
        }
    } else {
        
        
        
        if (rnd.next(1, 10) == 1) {
            
            n = rnd.next(1, 50);
        } else {
            
            n = rnd.next(200, 300);
        }
        
        start_val = rnd.next(1, 1000000);
        
        
        
        
        
        
        int strat = rnd.next(0, 3);
        
        
        if (mode == "adp" && strat == 0) strat = rnd.next(1, 3);

        for (int i = 0; i < n; i++) {
            int attempts = 0;
            while (true) {
                char op;
                int val;
                
                
                if (strat == 1) op = rnd.next(1, 10) > 2 ? '*' : '+';      
                else if (strat == 2) op = rnd.next(1, 10) > 2 ? '+' : '*'; 
                else op = rnd.next(0, 1) ? '+' : '*';                      
                
                
                val = rnd.next(1, 1000000);
                
                
                if (strat == 3) {
                    
                    if (op == '+' && rnd.next(0, 1)) {
                        val = rnd.next(1, 500000) * 2 - 1; 
                    }
                    
                    if (op == '*' && rnd.next(0, 1)) {
                        val = rnd.next(1, 500000) * 2;
                    }
                }

                if (try_add(op, val)) break;
                
                attempts++;
                if (attempts > 50) {
                    
                    op = rnd.next(0, 1) ? '+' : '*';
                    val = rnd.next(1, 1000000);
                    if (try_add(op, val)) break;
                }
            }
        }
    }
    
    
    
    
    
    
    
    println(n);
    for (const auto& p : cards) {
        println(p.first, p.second);
    }
    println(start_val);
    
    return 0;
}