Problem: cf1639_g
Difficulty: medium
Categories: graph, data structures
Time limit: 5000 ms, memory limit: 256 MB

All problems in this contest share the same statement, the only difference is the test your solution runs on. For further information on scoring please refer to "Scoring" section of the statement.

This is an interactive problem.

Imagine you are a treasure hunter, a very skillful one. One day you came across an ancient map which could help you to become rich. The map shows multiple forestry roads, and at each junction there is a treasure. So, you start your journey hoping to retrieve all the hidden treasures, but you don't know yet that there is a wicked wizard standing against you and craving to tangle up these roads and impede your achievements.

The treasure map is represented as an undirected graph in which vertices correspond to junctions and edges correspond to roads. Your path begins at a certain fixed vertex with a label known to you. Every time you come to a vertex that you have not been to before, you dig up a treasure chest and put a flag in this vertex. At the initial vertex you'll find a treasure chest immediately and, consequently, you'll put a flag there immediately as well.

When you are standing at the junction you can see for each of the adjacent vertices its degree and if there is a flag there. There are no other things you can see from there. Besides, the power of the wicked wizard is so great that he is able to change the location of the roads and junctions on the map without changing the graph structure. Therefore, the sequence of the roads coming from the junction $v$ might be different each time you come in the junction $v$. However, keep in mind that the set of adjacent crossroads does not change, and you are well aware of previously dug treasures at each adjacent to $v$ vertex.

Your goal is to collect treasures from all vertices of the graph as fast as you can. Good luck in hunting!

Interaction

On the first line the interactor prints an integer $t$ ($1 \leq t \leq 5$) — the number of maps for which you need to solve the problem.

Then for each of the $t$ maps interactor firstly prints the graph description and after that interaction on this map starts. The map description is given in the following format.

The first line contains four integers $n, m, start, base\_move\_count$ ($2 \leq n \leq 300$, $1 \leq m \leq min(\frac{n(n-1)}{2}, 25n)$, $1 \leq start \leq n$, $1000 \leq base\_move\_count \leq 30000$) — the number of vertices and edges in the graph, the label of the vertex where you start the journey and some base move count, on which the score for the map depends. It's guaranteed that the jury has a solution traversing the map using not more than $base\_move\_count$ moves with high probability.

The next $m$ lines contain descriptions of the edges of the graph $u, v$ ($1 \leq u, v \leq n$, $u \neq v$). It is guaranteed that the graph is connected, does not contain multiple edges and the degrees of all vertices do not exceed 50.

You can find map descriptions for all tests in the archive 'map\_descriptions.zip' which you can find in any of the archives 'problem-X-materials.zip' in the section "Contest materials" — they are all the same.

After that the interaction begins. The interactor prints vertex descriptions in the following format:

R~d~deg\_1~flag\_1~deg\_2~flag\_2 \ldots deg\_d~flag\_d, where $d$ is the degree of the current vertex, $deg_i$ is the degree of the $i$-th vertex adjacent to the current one, and $flag_i$ (0 or 1) is an indicator if the $i$-th adjacent vertex contains a flag. The order of neighbors in the vertex description is chosen by the interactor uniformly at random independently each time. Please keep in mind that you are not given the actual labels of adajacent vertices.

In response to the description of the vertex you should print a single integer $i$ ($1 \leq i \leq d$), which means that you have chosen the vertex with description $deg_i~flag_i$. Remember to use the flush operation after each output. If your output is invalid, you will get a "Wrong Answer" verdict.

When you have visited all the vertices of the graph at least once, the interactor prints the string "AC" instead of the vertex description. If you use more than $2 \cdot base\_move\_count$ moves, the interactor prints the string "F". In both cases, you have to either start reading the description of the next map, or terminate the program, if it has been the last map in the test.

Below the example of interaction is presented. Note that the test from the example is not the same that your solution will be tested on.

```
|  |  |
| --- | --- |
| Interactor | Solution |
| 1 |  |
| 3 3 1 1000 |  |
| 1 2 |  |
| 2 3 |  |
| 3 1 |  |
| R 2 2 0 2 0 |  |
|  | 1 |
| R 2 2 0 2 1 |  |
|  | 2 |
| R 2 2 0 2 1 |  |
|  | 1 |
| AC |  |
```

Scoring

All problems of this contest share the same statement and differ only in the test. Each problem contains one test. Map descriptions for the test are available to look at in the archive.

Each test consists of several maps.

The solution score for the problem will be $0$ in case your solution for some map failed to get an answer "AC" or "F". If the solution correctly interacted with the interactor on all $t$ maps, the score for the task will be equal to the sum of the scores for each of the maps.

If you have successfully passed all vertices of a graph with $n$ vertices using $moves$ moves, the score for the map is calculated as follows. Denote

base\\_fraction=\frac{base\\_move\\_count + 1}{n},sol\\_fraction=\frac{moves+1}{n},c=\frac{90}{\sqrt{base\\_fraction - 1}}.

Then:

* if $moves \leq base\_move\_count$, you get $100-c \sqrt{sol\_fraction - 1}$ points.
* if $base\_move\_count < moves \leq 2 \cdot base\_move\_count$, you get $20 - \frac{10\cdot(moves + 1)}{base\_move\_count + 1}$ points.

If you use more than $2 \cdot base\_move\_count$ moves, you get 0 points for the map.

For each problem the solution with the highest score is chosen. Please note that the maximum is chosen for the entire test as a whole, not for each separate map.

The final result of the participant is the sum of points for each of the problems.

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 mt19937 rng(42);

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

static int read_choice_index() {
    string token;
    if (!(cin >> token)) {
        finish(_wa, "Unexpected EOF while reading move");
    }
    if (token.empty()) {
        finish(_wa, "Invalid move token");
    }
    for (char ch : token) {
        if (ch < '0' || ch > '9') {
            finish(_wa, "Invalid move token");
        }
    }
    try {
        size_t pos = 0;
        long long v = stoll(token, &pos);
        if (pos != token.size() || v > numeric_limits<int>::max()) {
            finish(_wa, "Invalid move token");
        }
        return (int)v;
    } catch (...) {
        finish(_wa, "Invalid move token");
    }
    return -1;
}

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

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = inf.readInt(1, 5, "t");
    cout << t << endl;
    cout.flush();

    for (int tc = 0; tc < t; ++tc) {
        int n = inf.readInt(2, 300, "n");
        int max_m = (int)min(1LL * n * (n - 1) / 2, 25LL * n);
        int m = inf.readInt(1, max_m, "m");
        int start = inf.readInt(1, n, "start");
        int base_move_count = inf.readInt(1000, 30000, "base_move_count");

        long long map_limit = 2LL * base_move_count;
        query_limit += map_limit;
        if (tc == 0) log_metrics();

        vector<vector<int>> adj(n + 1);
        vector<int> deg(n + 1, 0);
        vector<pair<int, int>> edges;
        edges.reserve(m);

        for (int i = 0; i < m; ++i) {
            int u = inf.readInt(1, n, "u");
            int v = inf.readInt(1, n, "v");
            adj[u].push_back(v);
            adj[v].push_back(u);
            deg[u]++;
            deg[v]++;
            edges.push_back({u, v});
        }

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

        vector<char> visited(n + 1, 0);
        visited[start] = 1;
        int visited_count = 1;
        int cur = start;
        long long moves = 0;

        while (true) {
            if (visited_count == n) {
                cout << "AC" << endl;
                cout.flush();
                break;
            }
            if (moves > map_limit) {
                cout << "F" << endl;
                cout.flush();
                break;
            }

            vector<int> neighbors = adj[cur];
            shuffle(neighbors.begin(), neighbors.end(), rng);

            cout << "R " << neighbors.size();
            for (int v : neighbors) {
                cout << " " << deg[v] << " " << (visited[v] ? 1 : 0);
            }
            cout << endl;
            cout.flush();

            int choice = read_choice_index();
            if (choice < 1 || choice > (int)neighbors.size()) {
                finish(_wa, format("Invalid choice %d (expected 1..%d)", choice, (int)neighbors.size()));
            }

            cur = neighbors[choice - 1];
            moves++;
            queries++;
            if (queries % 200 == 0) log_metrics();

            if (!visited[cur]) {
                visited[cur] = 1;
                visited_count++;
            }
        }
    }

    finish(_ok, "All maps processed");
    return 0;
}


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 mt19937 rng(239);

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

static int read_choice_index() {
    string token;
    if (!(cin >> token)) {
        finish(_wa, "Unexpected EOF while reading move");
    }
    if (token.empty()) {
        finish(_wa, "Invalid move token");
    }
    for (char ch : token) {
        if (ch < '0' || ch > '9') {
            finish(_wa, "Invalid move token");
        }
    }
    try {
        size_t pos = 0;
        long long v = stoll(token, &pos);
        if (pos != token.size() || v > numeric_limits<int>::max()) {
            finish(_wa, "Invalid move token");
        }
        return (int)v;
    } catch (...) {
        finish(_wa, "Invalid move token");
    }
    return -1;
}

struct DisplayEntry {
    int v;
    int degree;
    int flag;
};

static void run_map(int n, int m, int start, int base_move_count, const vector<pair<int, int>>& edges) {
    vector<vector<int>> adj(n + 1);
    vector<int> deg(n + 1, 0);
    for (const auto& e : edges) {
        adj[e.first].push_back(e.second);
        adj[e.second].push_back(e.first);
        deg[e.first]++;
        deg[e.second]++;
    }

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

    long long map_limit = 2LL * base_move_count;
    vector<char> visited(n + 1, 0);
    visited[start] = 1;
    int visited_count = 1;
    int cur = start;
    long long moves = 0;

    while (true) {
        if (visited_count == n) {
            cout << "AC" << endl;
            cout.flush();
            return;
        }
        if (moves > map_limit) {
            cout << "F" << endl;
            cout.flush();
            return;
        }

        const vector<int>& neighbors = adj[cur];
        int d = (int)neighbors.size();

        vector<DisplayEntry> display;
        display.reserve(d);
        for (int v : neighbors) {
            display.push_back({v, deg[v], visited[v] ? 1 : 0});
        }
        shuffle(display.begin(), display.end(), rng);

        cout << "R " << d;
        for (const auto& e : display) {
            cout << " " << e.degree << " " << e.flag;
        }
        cout << endl;
        cout.flush();

        int choice = read_choice_index();
        if (choice < 1 || choice > d) {
            finish(_wa, format("Invalid choice %d (expected 1..%d)", choice, d));
        }

        cur = display[choice - 1].v;
        if (!visited[cur]) {
            visited[cur] = 1;
            visited_count++;
        }

        moves++;
        queries++;
        if (queries % 200 == 0) log_metrics();
        if (queries > 2000000) finish(_pe, "Hard query cap exceeded");
    }
}

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

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = inf.readInt(1, 5, "t");
    cout << t << endl;
    cout.flush();

    for (int tc = 0; tc < t; ++tc) {
        int n = inf.readInt(2, 300, "n");
        int max_m = (int)min(1LL * n * (n - 1) / 2, 25LL * n);
        int m = inf.readInt(1, max_m, "m");
        int start = inf.readInt(1, n, "start");
        int base_move_count = inf.readInt(1000, 30000, "base_move_count");

        query_limit += 2LL * base_move_count;
        if (tc == 0) log_metrics();

        vector<pair<int, int>> edges;
        edges.reserve(m);
        for (int i = 0; i < m; ++i) {
            int u = inf.readInt(1, n, "u");
            int v = inf.readInt(1, n, "v");
            edges.push_back({u, v});
        }

        run_map(n, m, start, base_move_count, edges);
    }

    finish(_ok, "All maps processed");
    return 0;
}


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

using namespace std;

static vector<pair<int, int>> gen_circulant_regular_graph(int n, int k) {
    
    
    if (k < 1) k = 1;
    if (k > 50) k = 50;
    if (k >= n) k = n - 1;
    if (k % 2 != 0) k--;
    if (k < 2) k = 2;

    vector<int> perm(n + 1);
    for (int i = 1; i <= n; ++i) perm[i] = i;
    shuffle(perm.begin() + 1, perm.end());

    set<pair<int, int>> edges;
    int half = k / 2;
    for (int i = 1; i <= n; ++i) {
        for (int delta = 1; delta <= half; ++delta) {
            int j = i + delta;
            if (j > n) j -= n;
            int u = perm[i];
            int v = perm[j];
            if (u > v) swap(u, v);
            edges.insert({u, v});
        }
    }

    return vector<pair<int, int>>(edges.begin(), edges.end());
}

static vector<pair<int, int>> gen_circulant_regular_graph_on_labels(const vector<int>& labels, int k) {
    
    int n = (int)labels.size();
    if (n <= 1) return {};

    if (k > 50) k = 50;
    if (k >= n) k = n - 1;
    if (k % 2 != 0) k--;
    if (k < 2) k = 2;

    vector<int> perm = labels;
    shuffle(perm.begin(), perm.end());

    set<pair<int, int>> edges;
    int half = k / 2;
    for (int i = 0; i < n; ++i) {
        for (int delta = 1; delta <= half; ++delta) {
            int j = i + delta;
            if (j >= n) j -= n;
            int u = perm[i];
            int v = perm[j];
            if (u > v) swap(u, v);
            edges.insert({u, v});
        }
    }

    return vector<pair<int, int>>(edges.begin(), edges.end());
}

struct TwoBlockGraph {
    vector<pair<int, int>> edges;
    int start = 1;
};

static TwoBlockGraph gen_two_block_bottleneck_graph(int n, int internal_k) {
    
    
    
    TwoBlockGraph out;
    if (n < 4) {
        out.edges = {{1, 2}, {2, 3}, {3, 4}};
        out.start = 1;
        return out;
    }

    int n1 = n / 2;
    int n2 = n - n1;

    internal_k = min(internal_k, 50);
    if (internal_k % 2 != 0) internal_k--;
    if (internal_k < 2) internal_k = 2;
    internal_k = min(internal_k, min(n1 - 1, n2 - 1));
    if (internal_k % 2 != 0) internal_k--;
    if (internal_k < 2) internal_k = 2;

    vector<int> labels_a(n1), labels_b(n2);
    iota(labels_a.begin(), labels_a.end(), 1);
    iota(labels_b.begin(), labels_b.end(), n1 + 1);

    vector<pair<int, int>> edges;
    {
        vector<pair<int, int>> ea = gen_circulant_regular_graph_on_labels(labels_a, internal_k);
        vector<pair<int, int>> eb = gen_circulant_regular_graph_on_labels(labels_b, internal_k);
        edges.reserve(ea.size() + eb.size() + 1);
        edges.insert(edges.end(), ea.begin(), ea.end());
        edges.insert(edges.end(), eb.begin(), eb.end());
    }

    int u = labels_a[rnd.next(0, n1 - 1)];
    int v = labels_b[rnd.next(0, n2 - 1)];
    if (u > v) swap(u, v);
    edges.push_back({u, v});

    
    vector<int> perm(n + 1);
    for (int i = 1; i <= n; ++i) perm[i] = i;
    shuffle(perm.begin() + 1, perm.end());

    for (auto& e : edges) {
        int a = perm[e.first];
        int b = perm[e.second];
        if (a > b) swap(a, b);
        e = {a, b};
    }
    sort(edges.begin(), edges.end());
    edges.erase(unique(edges.begin(), edges.end()), edges.end());

    
    int start_orig = labels_a[rnd.next(0, n1 - 1)];
    out.start = perm[start_orig];
    out.edges = std::move(edges);
    return out;
}

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

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

    
    int t = 5;
    cout << t << endl;

    for (int ti = 0; ti < t; ++ti) {
        int n = (mode == "adp") ? rnd.next(290, 300) : rnd.next(80, 300);

        int k;
        if (mode == "adp") {
            
            
            k = 48;
        } else {
            
            int max_k = min(50, n - 1);
            int min_k = min(10, max_k);
            k = rnd.next(min_k / 2, max_k / 2) * 2; 
            if (k < 2) k = 2;
            if (k >= n) k = n - 1 - ((n - 1) % 2);
            if (k < 2) k = 2;
        }

        vector<pair<int, int>> edges;
        int m = 0;
        int start = 1;
        if (mode == "adp") {
            TwoBlockGraph g = gen_two_block_bottleneck_graph(n, k);
            edges = std::move(g.edges);
            m = (int)edges.size();
            start = g.start;
        } else {
            edges = gen_circulant_regular_graph(n, k);
            m = (int)edges.size();
            start = rnd.next(1, n);
        }

        
        
        
        int base_move_count;
        if (mode == "adp") {
            base_move_count = 1000;
        } else {
            int base_low = 12 * n;
            int base_high = 18 * n;
            base_move_count = rnd.next(max(1000, base_low), min(30000, max(1000, base_high)));
        }

        
        cout << n << " " << m << " " << start << " " << base_move_count << endl;
        for (const auto& e : edges) {
            cout << e.first << " " << e.second << endl;
        }
    }

    return 0;
}