Problem: icpc2022_asia_yokohama_regional_b
Difficulty: easy
Categories: search, math
Time limit: 2000 ms, memory limit: 256 MB

This is an interactive problem.

Your task is to write a program that guesses a secret number through repetitive queries and responses. The secret number is a non-negative integer less than $1 0 ^ { 1 8 }$ .

Let $x$ be the secret number. In a query, you specify a non-negative integer $a$ . In response to this, the digit sum of $( x + a )$ will be returned. Here, the digit sum of a number means the sum of all the digits in its decimal notation. For example, the digit sum of 4096 is $4 + 0 + 9 + 6 = 1 9$ .

# Interaction

You should start with sending a query to the standard output and receiving the response from the standard input. This interaction can be repeated a number of times. When you have become confident of your guess through these interactions, you can send your answer.

A query should be in the following format, followed by a newline.

query $a$

Here, $a$ is an integer between 0 and $1 0 ^ { 1 8 } - 1$ , inclusive. In response to this query, the digit sum of $( x + a )$ , where $x$ is the secret number, is sent back to the standard input, followed by a newline.

You should send your answer to the standard output in the following format, followed by a newline.

answer $y$

Here, $y$ is the secret number you have identified, an integer between 0 and $1 0 ^ { 1 8 } - 1$ , inclusive.

You can send the answer only once, so you have to become sure of your guess through repeated interactions before sending the answer. However, you can send no more than 75 queries, and thus you have to choose your queries cleverly. After sending the answer, your program should terminate without any extra output.

# Notes on interactive judging

When your output violates any of the conditions above (invalid format, $a$ or $y$ being out of the range, too many queries, an extra output after sending your answer, and so on), your submission will be judged as a wrong answer. As some environments require flushing the output buffers, make sure that your outputs are actually sent. Otherwise, your outputs will never reach the judge.

![](images/4307c5b11e8043fa11d70eefc855cd43d417bbb10f3e9c091d7931656602dcb7.jpg)

In this example, the secret number $x$ is 75. In response to the first query, 15 is returned because $x + a = 7 5 + 3 = 7 8$ and its digit sum is $7 + 8 = 1 5$ . After the second response, you can conclude that the only possible secret number is 75.

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

using namespace std;


static long long queries = 0;
static long long query_limit = 75; 
static const long long HARD_CAP = 20000; 


long long get_digit_sum(long long n) {
    string s = to_string(n);
    long long sum = 0;
    for (char c : s) {
        sum += (c - '0');
    }
    return sum;
}



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 main(int argc, char* argv[]) {
    
    
    registerInteraction(argc, argv);
    
    
    atexit(log_metrics);

    
    
    long long x = inf.readLong();

    
    log_metrics();

    
    
    string token;
    while (cin >> token) {
        string lower_token = token;
        transform(lower_token.begin(), lower_token.end(), lower_token.begin(), ::tolower);

        bool is_query = false;
        bool is_answer = false;
        long long val = 0;

        
        
        if (lower_token == "query" || token == "?") {
            is_query = true;
            if (!(cin >> val)) {
                finish(_wa, "Unexpected EOF after query token");
            }
        } else if (lower_token == "answer" || token == "!") {
            is_answer = true;
            if (!(cin >> val)) {
                finish(_wa, "Unexpected EOF after answer token");
            }
        } else {
            
            
            if (isdigit(token[0]) || (token.size() > 1 && token[0] == '-' && isdigit(token[1]))) {
                try {
                    size_t pos;
                    val = stoll(token, &pos);
                    if (pos != token.size()) {
                        finish(_wa, "Invalid token format (trailing characters): " + token);
                    }
                    is_query = true;
                } catch (...) {
                    finish(_wa, "Invalid number format (parse error/overflow): " + token);
                }
            } else {
                finish(_wa, "Unknown token or invalid format: " + token);
            }
        }

        if (is_answer) {
            
            if (val < 0 || val >= 1000000000000000000LL) {
                finish(_wa, "Answer is out of range [0, 10^18 - 1]");
            }

            
            if (val == x) {
                finish(_ok, "Correct answer");
            } else {
                finish(_wa, "Wrong answer");
            }
        } else if (is_query) {
            
            if (val < 0 || val >= 1000000000000000000LL) {
                finish(_wa, "Query parameter is out of range [0, 10^18 - 1]");
            }

            
            queries++;

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

            
            if (queries % 10 == 0) {
                log_metrics();
            }

            
            long long resp = get_digit_sum(x + val);
            cout << resp << endl; 
        }
    }

    
    finish(_wa, "Submission terminated without providing an answer");

    return 0;
}


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

using namespace std;




long long queries = 0;
const long long QUERY_LIMIT = 75;
const long long HARD_CAP = 2000; 

bool adaptive = false;
long long fixed_secret = 0;
vector<long long> pool;







inline int get_digit_sum(long long n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}


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




void generate_pool() {
    
    
    
    
    
    pool.reserve(150000);
    mt19937_64 rng(1337); 

    
    auto add = [&](long long x) {
        if (x >= 0 && x < 1000000000000000000LL) {
            pool.push_back(x);
        }
    };

    
    for(int i = 0; i < 40000; ++i) {
        add(rng() % 1000000000000000000LL);
    }

    
    
    long long p10 = 1;
    for(int k = 0; k <= 18; ++k) {
        long long suffix = p10 - 1; 
        
        long long max_prefix = (1000000000000000000LL - 1 - suffix) / p10;
        
        if (max_prefix >= 0) {
            
            
            if (max_prefix < 2500) {
                for(long long pre = 0; pre <= max_prefix; ++pre) {
                    add(pre * p10 + suffix);
                }
            } else {
                for(int i = 0; i < 2500; ++i) {
                    long long prefix = rng() % (max_prefix + 1);
                    add(prefix * p10 + suffix);
                }
            }
        }
        if (k < 18) p10 *= 10;
    }

    
    for(int i = 0; i < 20000; ++i) {
        long long x = 0;
        for(int d = 0; d < 18; ++d) {
            int dig = rng() % 10;
            if (rng() % 3 == 0) dig = 9;
            x = x * 10 + dig;
        }
        add(x);
    }
    
    
    for(int i = 0; i < 10000; ++i) {
        long long x = 0;
        long long p = 1;
        for(int d = 0; d < 18; ++d) {
            if (rng() % 10 == 0) x += (rng() % 9 + 1) * p;
            p *= 10;
        }
        add(x);
    }

    
    add(0);
    add(1);
    add(1000000000000000000LL - 1);
    
    
    sort(pool.begin(), pool.end());
    pool.erase(unique(pool.begin(), pool.end()), pool.end());
}




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

    
    long long input_val = inf.readLong();
    
    
    if (input_val == 1000000000000000000LL) {
        adaptive = true;
        generate_pool();
    } else {
        adaptive = false;
        fixed_secret = input_val;
    }

    
    while (true) {
        string token;
        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF (no answer provided)");
        }

        if (token == "query" || token == "?") {
            long long a;
            if (!(cin >> a)) finish(_wa, "Invalid query parameter");
            
            
            if (a < 0 || a >= 1000000000000000000LL) {
                finish(_wa, "Query parameter out of range");
            }

            queries++;
            
            if (queries > QUERY_LIMIT) {
                finish(_wa, "Too many queries");
            }
            
            if (queries % 10 == 0) log_metrics();

            int response = 0;
            if (!adaptive) {
                response = get_digit_sum(fixed_secret + a);
            } else {
                
                
                
                
                
                
                
                static int freq[300]; 
                memset(freq, 0, sizeof(freq));
                
                for (long long cand : pool) {
                    freq[get_digit_sum(cand + a)]++;
                }
                
                int best_resp = -1;
                int max_count = -1;
                
                
                
                for (int v = 0; v < 300; ++v) {
                    if (freq[v] > max_count) {
                        max_count = freq[v];
                        best_resp = v;
                    }
                }
                
                if (best_resp == -1) {
                    
                    finish(_wa, "Interactor error: inconsistent state");
                }
                
                response = best_resp;
                
                
                int write_idx = 0;
                for (long long cand : pool) {
                    if (get_digit_sum(cand + a) == response) {
                        pool[write_idx++] = cand;
                    }
                }
                pool.resize(write_idx);
            }
            
            cout << response << endl; 

        } else if (token == "answer" || token == "!" || token == "ans") {
            long long y;
            if (!(cin >> y)) finish(_wa, "Invalid answer parameter");
            
            if (y < 0 || y >= 1000000000000000000LL) {
                finish(_wa, "Answer parameter out of range");
            }
            
            bool correct = false;
            if (!adaptive) {
                correct = (y == fixed_secret);
            } else {
                
                
                
                
                
                bool possible_alternative = false;
                for (long long cand : pool) {
                    if (cand != y) {
                        possible_alternative = true;
                        break;
                    }
                }
                
                if (possible_alternative) {
                    correct = false;
                } else {
                    
                    
                    
                    correct = true;
                }
            }
            
            if (correct) {
                finish(_ok, "Correct");
            } else {
                finish(_wa, "Wrong answer");
            }
            
        } else {
            finish(_wa, "Invalid protocol: expected 'query' or 'answer'");
        }
    }
    
    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");

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

    if (mode == "adp") {
        
        
        
        
        println(1000000000000000000LL);
    } else {
        
        
        
        long long MAX_VAL = 1000000000000000000LL;
        long long x = 0;

        
        if (seed_val == 1) {
            x = 0;
        } else if (seed_val == 2) {
            x = MAX_VAL - 1; 
        } else if (seed_val == 3) {
            x = 99999999999999999LL; 
        } else {
            
            int strategy = rnd.next(4);
            
            if (strategy == 0) {
                
                x = rnd.next(0LL, MAX_VAL - 1);
            } else if (strategy == 1) {
                
                x = 0;
                for (int i = 0; i < 18; ++i) {
                    int d = rnd.next(0, 9);
                    
                    if (rnd.next(4) == 0) d = 9;
                    x = x * 10 + d;
                }
            } else if (strategy == 2) {
                
                
                int num_nines = rnd.next(1, 17);
                long long suffix = 0;
                long long p = 1;
                for (int i = 0; i < num_nines; ++i) {
                    suffix += 9 * p;
                    p *= 10;
                }
                long long max_prefix = (MAX_VAL - 1 - suffix) / p;
                long long prefix = rnd.next(0LL, max_prefix);
                x = prefix * p + suffix;
            } else {
                
                
                x = 0;
                long long p = 1;
                for (int i = 0; i < 18; ++i) {
                    if (rnd.next(4) == 0) { 
                        x += rnd.next(1, 9) * p;
                    }
                    p *= 10;
                }
            }
        }
        println(x);
    }

    return 0;
}