Problem: icpc2025_amppz_g
Difficulty: hard
Categories: graph, game
Time limit: 3000 ms, memory limit: 1024 MB

This is an interactive problem.

The game "Plumber Mariusz” consists of $n { \mathrel { + { 1 } } }$ levels (numbered from 0 to $n$ ), each of which has $k$ versions (numbered from 0 to $k - 1$ ). Each version of every level (except the last one) ends with two pipes – the left and the right – and Mariusz must jump into one of them. Each pipe contains some number of coins and leads to one of the versions of the next level. Exactly two pipes lead to each version of each level (except level zero); these may be any two among the $2 \cdot k$ pipes coming out of all $k$ versions of the previous level.

A single playthrough consists of choosing a version of level zero and then choosing left or right pipe $n$ times, and the result of the playthrough is the total number of coins collected in the $n$ visited pipes. We do not learn how many coins were in each pipe or which versions of the levels were visited. Coins in the pipes always reset to their initial values, so all playthroughs are independent. A playthrough can therefore be represented by the function:

int rozgrywka(int ver, string s), where $v e r \in | 0 , k - 1 |$ and $s$ is a string of $n$ letters $\mathrm { L }$ and $\mathrm { P }$ .

Note that L and P correspond to the polish words for left and right respectively.

You may perform up to 300 000 test playthroughs and learn their results. After that, you will receive up to 10 000 queries about playthroughs. Your task is to determine their results.

Each test is fixed before the interaction begins (i.e. the interactor is not adaptive). A test contains a directed weighted graph with $( n + 1 ) \cdot k$ vertices arranged in $n + 1$ layers (levels), and q queries $( v e r , s )$ . Each vertex (except those in the last layer) has two outgoing edges (left and right), each described by a coin count $c \in [ 1 , 1 0 ^ { 8 } ]$ and a version $v \in [ 0 , k - 1 ]$ it leads to. Recall that every vertex except those in layer zero has in-degree exactly 2.

# Buffer flushing

After outputting each line you must flush the output buffer, so that the interactor receives the output line. For example, in $\mathrm { C } + +$ you may output a line containing an exclamation mark using cout << "!" << endl; or using printf $( " ! \setminus " )$ ; fflush(stdout);. In Python you may use print("!", flush=True). We do not guarantee stable behavior of the interactor if you output multiple lines without flushing after each of them, or if you output many lines without reading the interactor’s responses. Your program may not open any files.

# Interaction Protocol

The full communication flow is described below:

• Read the first line containing two integers $n$ and $k$ ( $1 \leq n , k \leq 2 0$ ) – there are $n + 1$ levels, each with $k$ versions.

• Up to 300 000 times, perform a test playthrough:

– Output a line of the form „? ver $\boldsymbol { \mathsf { s } } ^ { \flat }$ (without quotes), where $v e r \in [ 0 , k - 1 ]$ and $s$ is a string of $n$ letters L and P. Flush.   
– Read the next line containing the result of the playthrough – an integer $x$ $n \leq x \leq n \cdot 1 0 ^ { 8 } \leq 2 \cdot 1 0 ^ { 9 } ,$ ).

• Output a line containing a single character ! (exclamation mark). Flush.

• Read the next line containing an integer $q$ ( $1 \leq q \leq 1 0 0 0 0$ ) – the number of queries.

• For each of the $q$ queries:

– Read the next line containing an integer ver and a string $s$ (same limits as above).   
– Output a line containing the result of the playthrough (an integer). Flush.

![](images/36067e90a9f93909e8f7753c28a36331d0a3a04d23eaaa0470d411041486a6ed.jpg)

The illustration shows the first sample test ( $n = 2$ ; $k = 4$ ). Two final queries are marked using dashed lines: rozgrywka(0, "LL") $= 2 0 + 3 = 2 3$ and rozgrywka(1, "PL") $= 9 + 1 0 = 1 9$ .

# Example

Here is an example communication flow:

<table><tr><td>Your program</td><td>Interactor</td><td>Description</td></tr><tr><td rowspan="2">？O LL</td><td>24</td><td>3 levels (0...2),each with 4 versions (0...3).</td></tr><tr><td>23</td><td>First dashed path in the figure.Start from version O of level O, jump into the left pipe (20 coins). From version 1 of level 1 again jump into the left pipe (3 coins). Result: 2O+3= 23.</td></tr><tr><td>？O LL</td><td>23</td><td>Same playthrough, same result.</td></tr><tr><td>？2 PL</td><td>610</td><td>600 +10=610.</td></tr><tr><td>？OPP ？ 3 LP</td><td>9</td><td>5+4=9.</td></tr><tr><td>！</td><td>7</td><td>5+2=7.</td></tr><tr><td rowspan="4">23</td><td></td><td>End of test playthroughs.</td></tr><tr><td>2 OLL</td><td>There are q= 2 queries to answer.</td></tr><tr><td></td><td>Lucky - we tested this exact playthrough!</td></tr><tr><td>1 PL</td><td></td></tr><tr><td></td><td></td><td>We do not know the result and cannot deduce it. We never</td></tr><tr><td>19</td><td></td><td>tested starting from version 1 of level 0. By chance, we guess the correct result: 9 +10 = 19.</td></tr></table>

# Note

In the attachment (you can download it in the “attachment” tab at QOJ) you will find sample (incorrect) solutions G.cpp and G.py that perform the communication correctly, but compute the results incorrectly. The directory also includes: a sample checker Gsoc.cpp and sample tests (G0a.in, G0b.in, G0c.in, and a large random test G0d.in with $n = k = 2 0$ ). The format of test files is described in the comment at the beginning of Gsoc.cpp.

The sample checker Gsoc.cpp differs from the one used in QOJ. It might not validate input or the arguments of the playthrough function.

You can run your solution using the script run.sh. The command takes the compiled program and the test file name. For example, to run the sample programs on the sample test G0a.in:

• C++: ${ \tt S } ^ { + + }$ G.cpp -o G.e && ./run.sh "./G.e" G0a.in • Python: ./run.sh "python3 G.py" G0a.in

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

using namespace std;




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


struct Edge {
    int next_ver;
    int weight;
};



vector<vector<vector<Edge>>> adj;
int n, k;





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


long long solve_path(int ver, const string& s) {
    long long total = 0;
    int curr = ver;
    for (int i = 0; i < n; i++) {
        char c = s[i];
        int dir = (c == 'P' ? 1 : 0); 
        total += adj[i][curr][dir].weight;
        curr = adj[i][curr][dir].next_ver;
    }
    return total;
}

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

    
    
    n = inf.readInt(1, 20, "n");
    k = inf.readInt(1, 20, "k");

    
    adj.resize(n, vector<vector<Edge>>(k, vector<Edge>(2)));

    for (int i = 0; i < n; i++) {
        for (int u = 0; u < k; u++) {
            
            int vL = inf.readInt();
            int wL = inf.readInt();
            int vR = inf.readInt();
            int wR = inf.readInt();
            
            adj[i][u][0] = {vL, wL}; 
            adj[i][u][1] = {vR, wR}; 
        }
    }

    int q_count = inf.readInt(1, 10000, "q");
    vector<pair<int, string>> test_queries(q_count);
    for (int i = 0; i < q_count; i++) {
        test_queries[i].first = inf.readInt();
        test_queries[i].second = inf.readToken();
    }

    
    
    cout << n << " " << k << endl;

    
    
    long long hard_cap = 2000000;

    while (true) {
        string token;
        
        if (!(cin >> token)) {
            finish(_pe, "Unexpected EOF during Phase 1 (expected '?' or '!')");
        }

        if (token == "!") {
            break; 
        }

        int ver;
        string s;

        
        if (token == "?") {
            if (!(cin >> ver >> s)) {
                finish(_pe, "Invalid query format after '?'");
            }
        } else {
            
            bool is_num = !token.empty();
            for (char c : token) if (!isdigit(c)) is_num = false;
            
            if (is_num) {
                try {
                    ver = stoi(token);
                } catch (...) {
                    finish(_pe, "Token is not a valid integer: " + token);
                }
                if (!(cin >> s)) {
                    finish(_pe, "Missing string s after version");
                }
            } else {
                finish(_pe, "Unknown token (expected '?', '!', or integer): " + token);
            }
        }

        
        if (ver < 0 || ver >= k) {
            finish(_pe, "Version " + to_string(ver) + " out of range [0, " + to_string(k-1) + "]");
        }
        if ((int)s.size() != n) {
            finish(_pe, "String length must be exactly " + to_string(n));
        }
        for (char c : s) {
            if (c != 'L' && c != 'P') {
                finish(_pe, "String must contain only 'L' or 'P'");
            }
        }

        
        long long ans = solve_path(ver, s);
        cout << ans << endl;

        
        queries++;
        if (queries % 1000 == 0) log_metrics(); 
        if (queries > hard_cap) {
            finish(_pe, "Hard query limit exceeded (possible infinite loop)");
        }
    }

    
    
    cout << q_count << endl;

    for (int i = 0; i < q_count; i++) {
        int t_ver = test_queries[i].first;
        string t_s = test_queries[i].second;

        
        cout << t_ver << " " << t_s << endl;

        
        long long user_ans;
        if (!(cin >> user_ans)) {
            finish(_pe, "Expected integer answer from contestant for test query " + to_string(i+1));
        }

        
        long long correct = solve_path(t_ver, t_s);
        if (user_ans != correct) {
            finish(_wa, "Incorrect answer for query " + to_string(i+1) + 
                        ". Expected " + to_string(correct) + ", got " + to_string(user_ans));
        }
    }

    finish(_ok, "Passed all " + to_string(q_count) + " test queries.");
    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 = 0;
    if (argc > 1) {
        try {
            seed_val = stoll(string(argv[1]));
        } catch (...) {
            seed_val = 0; 
        }
    }

    int n = 0, k = 0, q = 0;

    
    if (seed_val == 1) {
        
        n = 1; k = 1; q = 5;
    } else if (seed_val == 2) {
        
        n = 20; k = 20; q = 10000;
    } else if (seed_val == 3) {
        
        n = 20; k = 1; q = 100;
    } else {
        
        n = rnd.next(1, 20);
        k = rnd.next(1, 20);
        
        
        if (rnd.next(3) == 0) q = rnd.next(1, 100);
        else q = rnd.next(100, 10000);
    }

    
    
    if (opt<int>("n", 0) != 0) n = opt<int>("n");
    if (opt<int>("k", 0) != 0) k = opt<int>("k");
    if (opt<int>("q", 0) != 0) q = opt<int>("q");

    
    n = clamp(n, 1, 20);
    k = clamp(k, 1, 20);
    q = clamp(q, 1, 10000);

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

    
    cout << n << " " << k << endl;

    
    
    
    
    
    for (int i = 0; i < n; i++) {
        
        vector<int> dests;
        dests.reserve(2 * k);
        for (int v = 0; v < k; v++) {
            dests.push_back(v);
            dests.push_back(v);
        }
        
        
        shuffle(dests.begin(), dests.end());

        
        for (int u = 0; u < k; u++) {
            int vL = dests[2 * u];
            int vR = dests[2 * u + 1];
            
            
            int wL = rnd.next(1, 100000000);
            int wR = rnd.next(1, 100000000);

            
            cout << vL << " " << wL << " " << vR << " " << wR << "\n";
        }
    }

    
    cout << q << endl;
    for (int i = 0; i < q; i++) {
        int ver = rnd.next(0, k - 1);
        string s;
        s.reserve(n);
        for (int j = 0; j < n; j++) {
            
            s += (rnd.next(0, 1) ? 'L' : 'P');
        }
        cout << ver << " " << s << "\n";
    }

    return 0;
}