Problem: icpc2022_nwerc_g
Difficulty: medium
Categories: data structures, math
Time limit: 2000 ms, memory limit: 256 MB

Hercule Poirot, world-renowned detective, was having a lovely cup of tea in his compartment on the Disorient Express when the train conductor rushed in. “We have lost track of the number of train carriages,” exclaimed the conductor. You see, this was no ordinary train, and had no first or last train carriage. Instead, the train carriages were connected to form a large cycle, causing the conductor’s confusion.

![](images/4f8efbe88056deacf570934998eeaa4624ce60de7e949f9170ba59956585cda8.jpg)  
Train. Unsplash License by Robin Ulrich on Unsplash

Hercule thought for a moment. “That is most peculiar,”

he said. “But I may be able to help you.” He reached over and grabbed the lamp from the side table. “You see, each train carriage has a light switch like this one. By moving between the carriages and toggling these switches, we can determine the number of train carriages.”

The conductor was sceptical, but agreed to try it. “We are in a hurry,” he said, “so please determine $n$ , the number of carriages that the train consists of, in at most $3 n + 5 0 0$ steps.” Here a step counts as either moving to an adjacent carriage or toggling a light switch in the current carriage. “The only thing I am certain of is that $n$ is at least 3 and at most 5000.”

# Interaction

This is an interactive problem. Your submission will be run against an interactor, which reads the standard output of your submission and writes to the standard input of your submission. This interaction needs to follow a specific protocol:

The interactor first sends an integer $s$ ( $s \in \{ 0 , 1 \}$ ), the state of the light switch in Hercule’s initial carriage.

Your submission may then send at most $3 n + 5 0 0 ~ \mathrm { s t e p s ^ { 1 } }$ for Hercule to carry out, each by printing one line of the form $\stackrel { 6 6 } { \mathop { \ : } } ? \ : \ : a ^ { \ : \prime \prime }$ , where $a$ is one of the following:

• “left”, to move to the adjacent train carriage in the clockwise direction, • “right”, to move to the adjacent train carriage in the counter-clockwise direction, or • “flip”, to toggle the light switch in the current train carriage.

After every step, the interactor replies with an integer $s$ $s \in \{ 0 , 1 \}$ ), the state of the light switch in the train carriage that Hercule is currently in, after he has carried out the requested step.

When you have determined the number of train carriages $n$ , print one line of the form “! $n$ ” ( $3 \leq n \leq 5 0 0 0$ ), after which the interaction will stop. Printing the answer does not count as a query.

The interactor is not adaptive; the initial state of the light switches is determined by the interactor before any interaction takes place. Make sure you flush the buffer after each write. Using more than $3 n + 5 0 0$ queries will result in a wrong answer verdict.

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

![](images/9d0d8564a4937ba26a449c9f93a1a135a524dc218865922274ab69a160028ac3.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 bool finished = false;




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


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

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

    
    
    int n = inf.readInt(3, 5000, "n");
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = inf.readInt(0, 1, format("a[%d]", i));
    }

    
    
    query_limit = 3LL * n + 500;
    
    
    log_metrics();

    
    int current_pos = 0;
    cout << a[current_pos] << endl;

    
    long long hard_cap = max(200000LL, 10 * query_limit);

    while (true) {
        string token;
        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF: contestant terminated without providing answer");
        }

        
        
        if (token.size() > 0 && token[0] == '!') {
            string ans_str;
            if (token.size() > 1) {
                ans_str = token.substr(1);
            } else {
                if (!(cin >> ans_str)) {
                    finish(_wa, "Unexpected EOF after '!'");
                }
            }

            int ans = 0;
            try {
                ans = stoi(ans_str);
            } catch (...) {
                finish(_pe, "Answer is not a valid integer");
            }

            if (ans == n) {
                
                
                
                finish(_ok, format("Correct! n=%d, queries=%lld", n, queries));
            } else {
                finish(_wa, format("Incorrect answer. Expected %d, got %d", n, ans));
            }
        }

        
        
        if (token == "?") {
            if (!(cin >> token)) {
                finish(_wa, "Unexpected EOF after '?'");
            }
        }

        
        queries++;
        
        
        if (queries % 100 == 0) {
            log_metrics();
        }

        
        if (queries > hard_cap) {
            finish(_pe, "Hard query limit exceeded (potential infinite loop)");
        }

        int res = -1;
        if (token == "left") {
            
            current_pos = (current_pos + 1) % n;
            res = a[current_pos];
        } else if (token == "right") {
            
            current_pos = (current_pos - 1 + n) % n;
            res = a[current_pos];
        } else if (token == "flip") {
            
            a[current_pos] = 1 - a[current_pos];
            res = a[current_pos];
        } else {
            finish(_pe, format("Invalid query token: '%s'", token.c_str()));
        }

        
        cout << res << endl;
    }

    return 0;
}


Adaptive interactor (C++):

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");

    
    long long seed_val = -1;
    if (argc > 1) {
        try {
            seed_val = stoll(string(argv[1]));
        } catch (...) {
            seed_val = -1; 
        }
    }

    int n;
    int type = -1;
    
    
    
    
    
    
    
    

    
    if (seed_val == 1) {
        n = 3; 
        type = 0; 
    } else if (seed_val == 2) {
        n = 5000; 
        type = 1; 
    } else if (seed_val == 3) {
        n = 5000; 
        type = 2; 
    } else if (seed_val == 4) {
        n = 5000; 
        type = 3; 
    } else if (seed_val == 5) {
        n = 5000; 
        type = 4; 
    } else if (seed_val == 6) {
        n = 5000; 
        type = 5; 
    } else if (seed_val == 7) {
        n = 4999; 
        type = 0; 
    } else {
        
        n = rnd.next(3, 5000);
        
        
        int r = rnd.next(1, 100);
        if (r <= 20) type = 0;      
        else if (r <= 30) type = 1; 
        else if (r <= 40) type = 2; 
        else if (r <= 50) type = 3; 
        else if (r <= 70) type = 4; 
        else if (r <= 90) type = 5; 
        else type = 6;              
    }

    
    if (mode == "adp") {
        cout << n << endl;
        return 0;
    }

    
    cout << n << endl;
    vector<int> a(n);

    if (type == 0) { 
        for (int i = 0; i < n; i++) a[i] = rnd.next(0, 1);
    } else if (type == 1) { 
        fill(a.begin(), a.end(), 0);
    } else if (type == 2) { 
        fill(a.begin(), a.end(), 1);
    } else if (type == 3) { 
        for (int i = 0; i < n; i++) a[i] = i % 2;
    } else if (type == 4) { 
        
        int max_p = max(2, min(n - 1, 50));
        int p = rnd.next(2, max_p);
        vector<int> pat(p);
        for (int i = 0; i < p; i++) pat[i] = rnd.next(0, 1);
        for (int i = 0; i < n; i++) a[i] = pat[i % p];
        
        int idx = rnd.next(0, n - 1);
        a[idx] = 1 - a[idx];
    } else if (type == 5) { 
        
        int min_p = max(2, n / 3);
        int max_p = max(min_p, n / 2 + 5);
        if (max_p >= n) max_p = n - 1;
        int p = rnd.next(min_p, max_p);
        vector<int> pat(p);
        for (int i = 0; i < p; i++) pat[i] = rnd.next(0, 1);
        for (int i = 0; i < n; i++) a[i] = pat[i % p];
        
        int idx = rnd.next(0, n - 1);
        a[idx] = 1 - a[idx];
    } else if (type == 6) { 
        fill(a.begin(), a.end(), 0);
        int k = rnd.next(1, max(1, n / 40));
        for (int i = 0; i < k; i++) a[rnd.next(0, n - 1)] = 1;
    }

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

    return 0;
}