Problem: icpc2024_asia_kunming_e
Difficulty: easy
Categories: graph, bit, math
Time limit: 1000 ms, memory limit: 1024 MB

This is an interactive problem.

Emily has a tree containing $n$ nodes, where the weight of the node numbered $i$ is $w _ { i }$ . Node 1 is the root node, and the weight of the root node is $0$ .

Emily wants you to guess the weight of each node. Specifically, you can make at most $n$ queries, with the $i$ -th query containing two integers $u _ { i }$ and $v _ { i }$ . If the simple path from $u _ { i }$ to $v _ { i }$ contains exactly $k$ edges, Emily will tell you the bitwise XOR $^ { \dag }$ of the weights of all the nodes on the simple path from node $u _ { i }$ to node $v _ { i }$ (including the endpoints). Otherwise, Emily will respond with “-1”, indicating that she does not want to answer that question. Emily is very busy, so she will not start answering until you have asked all your questions.

Of course, you may not be able to guess the weights of all nodes with no more than $n$ queries for some cases. In such cases, you should not make any queries and instead directly tell Emily that it is impossible.

†The bitwise XOR operation refers to the addition of each bit of two binary numbers under modulo 2.   
For example: $( 0 0 1 1 ) _ { 2 } \oplus ( 0 1 0 1 ) _ { 2 } = ( 0 1 1 0 ) _ { 2 }$ .

# Input

There is only one test case in each test file.

The first line contains two integers $n$ and $k$ ( $2 \leq n \leq 2 5 0$ , $1 \leq k \leq n - 1$ ), representing the number of nodes in the tree and a parameter mentioned in the query.

The next $n - 1$ lines each contain two integers $x _ { i }$ and $y _ { i }$ $1 \leq x _ { i } , y _ { i } \leq n )$ , indicating that there is an edge between nodes $x _ { i }$ and $y _ { i }$ .

It is guaranteed that all edges form a tree, and the correct answer satisfies $0 \leq w _ { i } < 2 ^ { 3 0 }$ .

# Interaction Protocol

First, you need to determine whether it is possible to guess the weights of all nodes within $n$ queries. If not, your program should output “No” and exit immediately. Otherwise, your program should output “Yes” and proceed to querying. You can output the answer in any case (upper or lower). For example, the strings “yEs”, “yes”, “Yes”, and “YES” will be recognized as positive responses.

To make a query, output all your $q$ queries at once in the format $^ {  } \ ? \ q \ u _ { 1 } \ v _ { 1 } \ u _ { 2 } \ v _ { 2 } \ \ldots \ u _ { q } \ v _ { q } \ ^ { , * }$ ( $1 \leq q \leq n$ , $1 \leq u _ { i } , v _ { i } \leq n _ { , }$ ). After flushing your output, your program should read a line containing $q$ integers. The $i$ -th integer represents the response to the $i$ -th query.

To guess the weights, output your guess in the format “ ! w2 w3 . . . $w _ { n } \ '$ ” ( $0 \leq w _ { i } < 2 ^ { 3 0 }$ ). After flushing your output, your program should exit immediately.

Note that the answer for each test case is pre-determined. That is, the interactor is not adaptive. Also, note that your guess does not count as a query.

To flush your output, you can use:

• fflush(stdout) (if you use printf) or cout.flush() (if you use cout) in C and $\mathrm { C } + +$

• System.out.flush() in Java and Kotlin.   
• stdout.flush() in Python.

# Examples

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>41</td><td></td></tr><tr><td>12</td><td></td></tr><tr><td>23</td><td></td></tr><tr><td>24</td><td></td></tr><tr><td></td><td>YES</td></tr><tr><td>132</td><td>？3122324</td></tr><tr><td></td><td>！123</td></tr><tr><td>52</td><td></td></tr><tr><td>12</td><td></td></tr><tr><td>23</td><td></td></tr><tr><td>34</td><td></td></tr><tr><td>35</td><td></td></tr><tr><td></td><td>YES</td></tr><tr><td>1234</td><td>？413242545</td></tr><tr><td></td><td>！4532</td></tr><tr><td>62</td><td></td></tr><tr><td>12</td><td></td></tr><tr><td>23</td><td></td></tr><tr><td>34</td><td></td></tr><tr><td>45</td><td></td></tr><tr><td></td><td></td></tr><tr><td>46</td><td></td></tr><tr><td></td><td>NO</td></tr></table>

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


static long long queries_count = 0;
static long long query_limit_val = 0;

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

void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}


string to_lower(string s) {
    for (char &c : s) c = tolower(c);
    return s;
}


int n, k;
vector<pair<int, int>> edges;
vector<vector<int>> adj;
vector<int> hidden_weights;
vector<int> parent_node;
int dist_mat[255][255]; 


void dfs_setup(int u, int p) {
    parent_node[u] = p;
    for (int v : adj[u]) {
        if (v != p) dfs_setup(v, u);
    }
}


void precompute_dist() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) dist_mat[i][j] = -1;
        dist_mat[i][i] = 0;
        queue<int> q;
        q.push(i);
        while (!q.empty()) {
            int u = q.front(); q.pop();
            for (int v : adj[u]) {
                if (dist_mat[i][v] == -1) {
                    dist_mat[i][v] = dist_mat[i][u] + 1;
                    q.push(v);
                }
            }
        }
    }
}


vector<int> get_path(int u, int v) {
    
    vector<int> p_u, p_v;
    int curr = u;
    while (curr) { p_u.push_back(curr); curr = parent_node[curr]; }
    curr = v;
    while (curr) { p_v.push_back(curr); curr = parent_node[curr]; }
    
    
    reverse(p_u.begin(), p_u.end());
    reverse(p_v.begin(), p_v.end());
    
    int lca = 0;
    size_t len = min(p_u.size(), p_v.size());
    for (size_t i = 0; i < len; i++) {
        if (p_u[i] == p_v[i]) lca = p_u[i];
        else break;
    }
    
    
    vector<int> res;
    
    curr = u;
    while (curr != lca) {
        res.push_back(curr);
        curr = parent_node[curr];
    }
    res.push_back(lca); 
    
    vector<int> tmp;
    curr = v;
    while (curr != lca) {
        tmp.push_back(curr);
        curr = parent_node[curr];
    }
    reverse(tmp.begin(), tmp.end());
    res.insert(res.end(), tmp.begin(), tmp.end());
    
    return res;
}


bitset<256> basis[256];
int basis_sz = 0;

void insert_basis(const bitset<256>& mask) {
    bitset<256> curr = mask;
    
    
    for (int i = 0; i < n - 1; i++) {
        if (!curr[i]) continue;
        if (!basis[i].any()) {
            basis[i] = curr;
            basis_sz++;
            return;
        }
        curr ^= basis[i];
    }
}


bool is_solvable() {
    for (int i = 0; i < 256; i++) basis[i].reset();
    basis_sz = 0;
    
    
    for (int u = 1; u <= n; u++) {
        for (int v = u + 1; v <= n; v++) { 
            if (dist_mat[u][v] == k) {
                vector<int> p = get_path(u, v);
                bitset<256> mask;
                for (int node : p) {
                    if (node >= 2) {
                        
                        mask[node - 2] = 1;
                    }
                }
                insert_basis(mask);
            }
        }
    }
    
    return (basis_sz == n - 1);
}

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

    
    n = inf.readInt();
    k = inf.readInt();
    
    adj.resize(n + 1);
    for (int i = 0; i < n - 1; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        edges.push_back({u, v});
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    
    hidden_weights.resize(n + 1);
    for (int i = 1; i <= n; i++) {
        hidden_weights[i] = inf.readInt();
    }
    
    hidden_weights[1] = 0;

    
    query_limit_val = n;

    
    parent_node.resize(n + 1);
    dfs_setup(1, 0);
    precompute_dist();
    bool solvable = is_solvable();

    
    cout << n << " " << k << endl;
    for (auto &e : edges) {
        cout << e.first << " " << e.second << endl;
    }
    cout.flush();

    
    string token;
    if (!(cin >> token)) finish(_wa, "Unexpected EOF while reading YES/NO");
    string s = to_lower(token);
    
    if (s == "no") {
        if (solvable) finish(_wa, "Contestant said NO, but the weights are uniquely determined");
        else { finish(_ok, "Correctly identified impossible case"); return 0; }
    } else if (s == "yes") {
        if (!solvable) finish(_wa, "Contestant said YES, but the weights are not uniquely determined");
    } else {
        finish(_wa, "Expected 'YES' or 'NO'");
    }

    
    
    if (!(cin >> token)) finish(_wa, "Unexpected EOF after YES");
    
    if (token == "?") {
        int q;
        if (!(cin >> q)) finish(_wa, "Expected query count q after ?");
        
        
        if (q < 1 || q > n) finish(_wa, format("Invalid query count %d (must be 1..n)", q));
        
        queries_count += q;
        log_metrics(); 

        
        vector<int> answers;
        for (int i = 0; i < q; i++) {
            int u, v;
            if (!(cin >> u >> v)) finish(_wa, "Expected query pair u v");
            if (u < 1 || u > n || v < 1 || v > n) finish(_wa, "Invalid node index in query");
            
            if (dist_mat[u][v] == k) {
                int res = 0;
                for (int node : get_path(u, v)) res ^= hidden_weights[node];
                answers.push_back(res);
            } else {
                answers.push_back(-1);
            }
        }
        
        
        for (int i = 0; i < q; i++) {
            cout << answers[i] << (i == q - 1 ? "" : " ");
        }
        cout << endl; 
        
        
        if (!(cin >> token)) finish(_wa, "Expected '!' after query responses");
    } 
    
    
    if (token != "!") finish(_wa, "Expected '!' to output answer");
    
    
    for (int i = 2; i <= n; i++) {
        int w_guess;
        if (!(cin >> w_guess)) finish(_wa, "Expected weight guess");
        if (w_guess != hidden_weights[i]) {
            finish(_wa, format("Wrong weight for node %d. Expected %d, got %d", i, hidden_weights[i], w_guess));
        }
    }
    
    finish(_ok, "Weights are correct");
    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");
    int n = opt<int>("n", -1);
    int k = opt<int>("k", -1);
    int t = opt<int>("t", -1); 

    
    
    long long seed_val = -1;
    if (argc > 1) {
        string s = argv[1];
        if (s.find_first_not_of("0123456789") == string::npos && s.size() < 18) {
            try {
                seed_val = stoll(s);
            } catch (...) {}
        }
    }

    
    if (n == -1) {
        if (seed_val == 1) { n = 2; k = 1; t = 1; }      
        else if (seed_val == 2) { n = 3; k = 2; t = 2; } 
        else if (seed_val == 3) { n = 5; k = 4; t = 1; } 
        else if (seed_val == 4) { n = 10; k = 1; t = 0; }
        else if (seed_val != -1 && seed_val <= 10) { 
            n = rnd.next(2, 20); 
        } else {
            
            if (rnd.next(5) == 0) n = rnd.next(2, 50);
            else n = rnd.next(200, 250);
        }
    }

    
    n = max(2, min(250, n));

    
    if (t == -1) t = rnd.next(5);

    
    
    vector<pair<int,int>> edges;
    vector<int> p(n);
    iota(p.begin(), p.end(), 0);
    shuffle(p.begin(), p.end()); 

    if (t == 0) { 
        for (int i = 1; i < n; i++) 
            edges.push_back({p[i], p[rnd.next(i)]});
    } else if (t == 1) { 
        for (int i = 1; i < n; i++) 
            edges.push_back({p[i], p[i-1]});
    } else if (t == 2) { 
        int center = p[0];
        for (int i = 1; i < n; i++) 
            edges.push_back({p[i], center});
    } else if (t == 3) { 
        for (int i = 1; i < n; i++) 
            edges.push_back({p[i], p[(i-1)/2]});
    } else { 
        int len = rnd.next(2, n);
        
        for (int i = 1; i < len; i++) edges.push_back({p[i], p[i-1]});
        
        for (int i = len; i < n; i++) edges.push_back({p[i], p[rnd.next(len)]});
    }
    
    
    shuffle(edges.begin(), edges.end());

    
    if (k == -1) {
        int roll = rnd.next(100);
        if (roll < 10) k = 1;
        else if (roll < 20) k = 2;
        else if (roll < 35) k = n - 1; 
        else if (roll < 70) k = rnd.next(1, n - 1);
        else k = rnd.next(max(1, n/2), n - 1); 
    }
    
    k = max(1, min(n - 1, k));

    
    cout << n << " " << k << "\n";
    
    for (auto& e : edges) {
        cout << (e.first + 1) << " " << (e.second + 1) << "\n";
    }

    
    if (mode == "non") {
        vector<int> w(n + 1);
        w[1] = 0; 
        
        for (int i = 2; i <= n; i++) {
            w[i] = rnd.next(0, 1073741823); 
        }
        
        for (int i = 1; i <= n; i++) {
            cout << w[i] << (i == n ? "" : " ");
        }
        cout << "\n";
    }

    return 0;
}