Problem: icpc2022_gcpc_d
Difficulty: medium
Categories: search, math
Time limit: 1000 ms, memory limit: 256 MB

Each year, the fastest platypuses of the world come together for the Great Crawling Platypus Cup (GCPC) to crawl a single lap in the platypus arena. Of course, Perry the platypus wants to take part in this great event. Perry’s arch enemy Dr. Doofenshmirtz got wind of it and plans to capture Perry right when he crosses the finish line at the GCPC. To carry out his evil plan successfully, he needs to find out how long Perry needs to finish the lap. He knows that Perry takes the preparation for the GCPC very seriously and trains by crawling lap after lap in the arena at a constant speed of $1 \%$ . Perry’s training starts this Saturday and Dr. Doofenshmirtz knows that he will run at least $1 0 ^ { 1 8 }$ seconds. To determine the exact length of one lap, Dr. Doofenshmirtz invented the Measurinator which can measure the exact distance Perry has already crawled in his current lap. Unfortunately, the Measurinator will break after too many measurements, so Dr. Doofenshmirtz has to carefully plan when to use it in order to determine the length of one lap.

![](images/3fc0b1d74356080949ea48c757d6fd3c3b0ca7fff68747158119970d76be1253.jpg)  
A circular running track. Illustration by Kjrstie, Pixabay

# Interaction

Your submission can print “? t” where $t$ $0 \leq t < 1 0 ^ { 1 8 }$ ) is the point in time when you want to use the Measurinator. The time $t = 0$ marks the point in time where Perry starts practicing i.e. where he starts his first lap at the start and finish line. The Measurinator responds with a single integer $x$ which is the current position of Perry in his current lap. Note that the Measurinator will not respond with the number of meters Perry has already crawled in total! Thus, whenever Perry reaches the start and finish line, the Measurinator will respond with 0. Further note that you recently lost your Timetravellinator and therefore, the values of $t$ must be strictly increasing.

If you determined the length $x$ of one lap, print “! x”. After this, your program should immediately terminate.

All interactions must be ended by a newline. Further note that you additionally need to flush the standard output to ensure that the query is sent. For example, you can use:

• std::cout $< <$ std::flush in $\mathrm { C } { + + }$ • fflush(stdout) in C • System.out.flush() in Java • sys.stdout.flush() in Python

Your submission is considered correct if you followed these rules, found the correct length of one lap, and used no more than 42 queries. Note that giving the answer is also counted as a query.

It is guaranteed that one lap has integer length, is at least one meter long, and at most $1 0 ^ { 1 2 }$ meters long.

A testing tool is provided to help you develop your solution. It can be downloaded from the DOMjudge problems overview page.

<table><tr><td rowspan=1 colspan=3>Read                                   Sample Interaction 1                                   Write?1</td></tr><tr><td rowspan=1 colspan=2>1</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？2</td></tr><tr><td rowspan=1 colspan=2>2</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？3</td></tr><tr><td rowspan=1 colspan=2>3</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？4</td></tr><tr><td rowspan=1 colspan=2>4</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？100</td></tr><tr><td rowspan=1 colspan=2>16</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>！42</td></tr></table>

<table><tr><td rowspan=1 colspan=3>Read                                   Sample Interaction 2                                   Write？10</td></tr><tr><td rowspan=1 colspan=2>10</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？20</td></tr><tr><td rowspan=1 colspan=2>20</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？30</td></tr><tr><td rowspan=1 colspan=2>30</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？40</td></tr><tr><td rowspan=1 colspan=2>40</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>？10000</td></tr><tr><td rowspan=1 colspan=2>641</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=2>！1337</td></tr></table>

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

using namespace std;


static long long queries = 0;
static const long long query_limit = 42; 


static long long C;
static long long last_t = -1;



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());
}


long long parse_long(const string& s) {
    try {
        size_t idx = 0;
        long long val = stoll(s, &idx);
        if (idx != s.size()) {
            finish(_pe, format("Token '%s' contains invalid characters", s.c_str()));
        }
        return val;
    } catch (const out_of_range&) {
        finish(_pe, format("Number '%s' is out of range", s.c_str()));
    } catch (...) {
        finish(_pe, format("Cannot parse '%s' as integer", s.c_str()));
    }
    return 0; 
}

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

    
    
    C = inf.readLong(1LL, 1000000000000LL, "C");

    
    
    
    int hard_cap = 2000;

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

        string token;
        
        if (!(cin >> token)) {
            finish(_pe, "Unexpected EOF: solution finished without outputting answer '! x'");
        }

        bool is_answer = false;
        long long val = 0;

        
        if (token == "!") {
            
            is_answer = true;
            if (!(cin >> val)) {
                finish(_pe, "Expected integer after '!'");
            }
        } else if (token == "?") {
            
            is_answer = false;
            if (!(cin >> val)) {
                finish(_pe, "Expected integer after '?'");
            }
        } else if (token.size() > 1 && token[0] == '!') {
            
            is_answer = true;
            val = parse_long(token.substr(1));
        } else if (token.size() > 1 && token[0] == '?') {
            
            is_answer = false;
            val = parse_long(token.substr(1));
        } else {
            
            
            is_answer = false;
            val = parse_long(token);
        }

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

        if (is_answer) {
            
            if (val == C) {
                finish(_ok, "Correct answer");
            } else {
                finish(_wa, format("Wrong answer: expected %lld, found %lld", C, val));
            }
        } else {
            
            
            
            if (val < 0) {
                finish(_pe, format("Query t must be non-negative, got %lld", val));
            }
            if (val >= 1000000000000000000LL) {
                finish(_pe, format("Query t must be < 10^18, got %lld", val));
            }

            
            
            if (val <= last_t) {
                finish(_pe, format("Query t must be strictly increasing: prev=%lld, curr=%lld", last_t, val));
            }

            last_t = val;

            
            long long resp = val % C;
            cout << resp << endl; 
        }
    }

    return 0;
}


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

using namespace std;


static long long queries_count = 0;
static long long query_limit = 42;


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


template<typename... Args>
void finish(TResult verdict, const char* fmt, Args... args) {
    log_metrics();
    quitf(verdict, fmt, args...);
}


vector<long long> candidates;
long long last_t = -1;

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

    
    
    
    vector<long long> params;
    while (!inf.seekEof()) {
        params.push_back(inf.readLong());
    }

    long long L = 1, R = 1000000000000LL;
    if (params.size() >= 2) {
        L = params[0];
        R = params[1];
    } else if (params.size() == 1) {
        L = params[0];
        R = params[0];
    }
    
    
    if (R > 1000000000000LL) R = 1000000000000LL;
    if (L < 1) L = 1;
    if (L > R) L = R; 

    
    
    {
        set<long long> s;
        auto add = [&](long long x) {
            if (x >= L && x <= R) s.insert(x);
        };

        
        for (long long i = 0; i <= 2000; ++i) {
            add(L + i);
            if (R - i >= L) add(R - i);
        }
        for (long long i = 1; i <= 2000; ++i) add(i);

        
        for (long long p = 1; p <= R; p *= 2) add(p);
        for (long long p = 1; p <= R; p *= 10) add(p);
        
        
        vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
        for (int i = 0; i < 5000; ++i) {
            long long curr = 1;
            int ops = 0;
            while (ops < 60) {
                int p = primes[rnd.next(primes.size())];
                if (__builtin_mul_overflow(curr, p, &curr)) break;
                if (curr > R) break;
                if (curr >= L) s.insert(curr);
                ops++;
            }
        }

        
        
        while (s.size() < 150000) {
            s.insert(rnd.next(L, R));
        }

        candidates.assign(s.begin(), s.end());
        shuffle(candidates.begin(), candidates.end());
    }

    
    log_metrics();

    
    while (true) {
        
        if (queries_count > 200) {
            finish(_pe, "Too many queries (>200)");
        }

        string line;
        char c;
        
        while (cin.get(c) && isspace(c));
        if (cin.eof()) finish(_pe, "Unexpected EOF");
        line += c;
        string rest;
        getline(cin, rest);
        line += rest;

        stringstream ss(line);
        char type;
        ss >> type;
        
        queries_count++;

        if (type == '?') {
            long long t;
            if (!(ss >> t)) finish(_pe, "Invalid query format");

            
            if (t < 0 || t >= 1000000000000000000LL) finish(_pe, "t out of bounds [0, 10^18)");
            if (t <= last_t) finish(_pe, "t must be strictly increasing");
            last_t = t;

            
            
            static vector<pair<long long, int>> rems;
            rems.clear();
            rems.reserve(candidates.size());
            
            for (int i = 0; i < (int)candidates.size(); ++i) {
                rems.push_back({t % candidates[i], i});
            }
            sort(rems.begin(), rems.end());

            long long best_rem = -1;
            int max_cnt = -1;
            int cur_cnt = 0;
            long long cur_val = -1;

            
            for (size_t i = 0; i < rems.size(); ++i) {
                if (i == 0 || rems[i].first != rems[i-1].first) {
                    if (cur_cnt > max_cnt) {
                        max_cnt = cur_cnt;
                        best_rem = cur_val;
                    }
                    cur_val = rems[i].first;
                    cur_cnt = 1;
                } else {
                    cur_cnt++;
                }
            }
            if (cur_cnt > max_cnt) {
                max_cnt = cur_cnt;
                best_rem = cur_val;
            }

            
            vector<long long> next_pool;
            next_pool.reserve(max_cnt);
            
            auto it = lower_bound(rems.begin(), rems.end(), make_pair(best_rem, -1));
            while (it != rems.end() && it->first == best_rem) {
                next_pool.push_back(candidates[it->second]);
                it++;
            }
            candidates = move(next_pool);

            
            cout << best_rem << endl;

        } else if (type == '!') {
            long long ans;
            if (!(ss >> ans)) finish(_pe, "Invalid answer format");

            
            bool found = false;
            long long other = -1;
            for (long long x : candidates) {
                if (x == ans) found = true;
                else other = x;
            }

            if (!found) {
                
                finish(_wa, "Answer %lld not consistent with tracked candidates", ans);
            }
            if (other != -1) {
                
                finish(_wa, "Ambiguous: valid candidates include %lld and %lld", ans, other);
            }
            
            
            finish(_ok, "Correct");
        } else {
            finish(_pe, "Unknown request type '%c'", type);
        }
        
        
        if (queries_count % 10 == 0) log_metrics();
    }
}


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

using namespace std;


bool is_integer(const string& s) {
    if (s.empty()) return false;
    for (char c : s) if (!isdigit(c)) return false;
    return true;
}

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

    
    string mode = opt<string>("mode", "non");

    const long long MIN_C = 1;
    const long long MAX_C = 1'000'000'000'000LL;

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

    
    long long C = 1;
    long long seedVal = -1;

    
    if (argc > 1 && is_integer(argv[1])) {
        try {
            seedVal = stoll(argv[1]);
        } catch (...) {
            seedVal = -1;
        }
    }

    
    if (seedVal >= 1 && seedVal <= 15) {
        switch (seedVal) {
            case 1: C = MIN_C; break; 
            case 2: C = MAX_C; break; 
            case 3: C = MAX_C - 1; break; 
            case 4: C = 999999999989LL; break; 
            case 5: C = 1LL << 39; break; 
            case 6: 
                {
                    C = 1;
                    vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
                    for (int p : primes) {
                        if (MAX_C / p >= C) C *= p;
                    }
                    
                    while (MAX_C / 2 >= C) C *= 2;
                }
                break;
            case 7: C = 2; break;
            case 8: C = 42; break;
            case 9: C = 100; break;
            case 10: C = 123456789123LL; break; 
            case 11: C = 1000000007; break; 
            case 12: C = rnd.next(MAX_C / 2, MAX_C); break; 
            case 13: C = rnd.next(MIN_C, 1000LL); break; 
            case 14: C = rnd.next(MAX_C - 10000, MAX_C); break; 
            case 15: C = 998244353; break; 
        }
    } else {
        
        int type = rnd.next(4);
        if (type == 0) {
            
            C = rnd.next(MIN_C, 1000000LL);
        } else if (type == 1) {
            
            C = rnd.next(1000000LL, 1000000000LL);
        } else if (type == 2) {
            
            C = rnd.next(1000000000LL, MAX_C);
        } else {
            
            C = rnd.next(MIN_C, MAX_C);
        }
    }

    
    if (C < MIN_C) C = MIN_C;
    if (C > MAX_C) C = MAX_C;

    cout << C << endl;

    return 0;
}