Problem: cf1639_d
Difficulty: hard
Categories: graph, greedy
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;


const long long HARD_CAP = 1000000;


struct LCG {
    unsigned long long state;
    LCG(unsigned long long seed = 1) : state(seed) {}
    unsigned int next() {
        state = state * 6364136223846793005ULL + 1;
        return state >> 33; 
    }
    int next(int r) { 
        if (r <= 0) return 0;
        return next() % r;
    }
};

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

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

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

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

    
    LCG rng(163916391639ULL); 

    for (int map_idx = 0; map_idx < t; ++map_idx) {
        
        int n = inf.readInt(2, 300, "n");
        
        
        int max_possible_edges = n * (n - 1) / 2;
        int max_constraint = 25 * n;
        int max_m = min(max_possible_edges, max_constraint);
        
        int m_edges = 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;
        log_metrics(); 

        
        vector<vector<int>> adj(n + 1);
        vector<pair<int,int>> edges_list;
        edges_list.reserve(m_edges);
        
        for (int i = 0; i < m_edges; ++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);
            edges_list.push_back({u, v});
        }

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

        
        int current_u = start;
        vector<bool> visited(n + 1, false);
        visited[start] = true;
        int visited_cnt = 1;
        int moves_this_map = 0;
        int limit_this_map = 2 * base_move_count;

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

            
            if (moves_this_map > limit_this_map) {
                cout << "F" << endl;
                break; 
            }

            
            if (visited_cnt == n) {
                cout << "AC" << endl;
                break; 
            }

            
            const auto& neighbors = adj[current_u];
            int d = neighbors.size();
            vector<NeighborInfo> n_infos;
            n_infos.reserve(d);
            for (int v : neighbors) {
                n_infos.push_back({v, (int)adj[v].size(), visited[v] ? 1 : 0});
            }

            
            for (int i = d - 1; i > 0; --i) {
                int j = rng.next(i + 1);
                swap(n_infos[i], n_infos[j]);
            }

            
            cout << "R " << d;
            for (const auto& info : n_infos) {
                cout << " " << info.degree << " " << info.flag;
            }
            cout << endl; 

            
            string token;
            if (!(cin >> token)) {
                finish(_pe, "Unexpected EOF or read error waiting for move");
            }
            
            
            int choice = -1;
            try {
                size_t pos;
                choice = stoi(token, &pos);
                if (pos != token.size()) {
                    finish(_pe, "Invalid token (trailing characters): " + token);
                }
            } catch (...) {
                finish(_pe, "Invalid token (not an integer): " + token);
            }

            
            if (choice < 1 || choice > d) {
                finish(_wa, format("Invalid choice %d, expected [1, %d]", choice, d));
            }

            
            int next_v = n_infos[choice - 1].v;
            current_u = next_v;
            moves_this_map++;
            queries++;

            
            if (!visited[current_u]) {
                visited[current_u] = true;
                visited_cnt++;
            }
            
            
            if (queries % 200 == 0) log_metrics();
        }
    }

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

struct Neighbor {
    int id;
    int degree;
    bool has_flag;
};

void run_map() {
    
    int n = inf.readInt(2, 300, "n");
    
    
    int m = inf.readInt(1, n * (n - 1) / 2, "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;

    
    vector<vector<int>> adj(n + 1);
    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);
        edges.push_back({u, v});
    }

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

    
    vector<bool> has_flag(n + 1, false);
    int visited_count = 0;

    
    has_flag[start] = true;
    visited_count = 1;

    int curr = start;
    int moves = 0;
    int max_moves = 2 * base_move_count;

    
        while (true) {
            
            if (visited_count == n) {
                cout << "AC" << endl;
                return;
            }

            
            
            if (moves > max_moves) {
                cout << "F" << endl;
                return;
            }

        
        vector<int>& neighbors = adj[curr];
        vector<Neighbor> desc;
        desc.reserve(neighbors.size());
        for (int v : neighbors) {
            desc.push_back({v, (int)adj[v].size(), has_flag[v]});
        }

        
        
        shuffle(desc.begin(), desc.end());

        
        cout << "R " << desc.size();
        for (const auto& nb : desc) {
            cout << " " << nb.degree << " " << (nb.has_flag ? 1 : 0);
        }
        cout << endl;

        
        int choice = 0;
        cin >> choice;
        if (cin.fail()) {
            finish(_wa, "Failed to read integer choice");
        }
        if (choice < 1 || choice > (int)desc.size()) {
            finish(_wa, format("Invalid choice %d, expected [1, %d]", choice, (int)desc.size()));
        }

        
        curr = desc[choice - 1].id;
        moves++;
        queries++; 

        if (!has_flag[curr]) {
            has_flag[curr] = true;
            visited_count++;
        }

        
        if (queries > 3000000) {
            finish(_pe, "Hard query limit exceeded (3,000,000)");
        }
    }
}

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

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

    
    for (int i = 0; i < t; ++i) {
        run_map();
    }

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


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

using namespace std;

const int MAXN = 300;
const int MAX_DEG = 50;

struct Edge {
    int u, v;
    bool operator<(const Edge& other) const {
        if (u != other.u) return u < other.u;
        return v < other.v;
    }
};

bool is_connected(int n, const vector<Edge>& edges) {
    if (n <= 1) return true;
    if (edges.empty()) return false;
    vector<vector<int>> adj(n + 1);
    for (const auto& e : edges) {
        adj[e.u].push_back(e.v);
        adj[e.v].push_back(e.u);
    }
    vector<bool> vis(n + 1, false);
    vector<int> q;
    q.reserve(n);
    q.push_back(1);
    vis[1] = true;
    int head = 0;
    while(head < (int)q.size()){
        int u = q[head++];
        for(int v : adj[u]){
            if(!vis[v]){
                vis[v] = true;
                q.push_back(v);
            }
        }
    }
    for (int i = 1; i <= n; ++i) if (!vis[i]) return false;
    return true;
}

bool check_degrees(int n, const vector<Edge>& edges) {
    vector<int> deg(n + 1, 0);
    for (const auto& e : edges) {
        deg[e.u]++;
        deg[e.v]++;
    }
    for (int i = 1; i <= n; ++i) if (deg[i] > MAX_DEG) return false;
    return true;
}

vector<Edge> gen_random_graph(int n, int m_target) {
    vector<Edge> edges;
    vector<int> p(n);
    iota(p.begin(), p.end(), 1);
    shuffle(p.begin(), p.end());
    
    set<Edge> edge_set;
    vector<int> deg(n + 1, 0);
    
    
    for (int i = 1; i < n; ++i) {
        int u = p[i];
        int v = p[rnd.next(0, i - 1)];
        if (u > v) swap(u, v);
        
        
        if (deg[u] < MAX_DEG && deg[v] < MAX_DEG) {
            edge_set.insert({u, v});
            deg[u]++; deg[v]++;
        } else {
             
             for(int j=0; j<i; ++j){
                 int candidate = p[j];
                 int uu = u, vv = candidate;
                 if (uu > vv) swap(uu, vv);
                 if(deg[uu] < MAX_DEG && deg[vv] < MAX_DEG){
                     edge_set.insert({uu, vv});
                     deg[uu]++; deg[vv]++;
                     break;
                 }
             }
        }
    }
    
    
    int attempts = 0;
    while ((int)edge_set.size() < m_target && attempts < 20000) {
        attempts++;
        int u = rnd.next(1, n);
        int v = rnd.next(1, n);
        if (u == v) continue;
        if (u > v) swap(u, v);
        if (edge_set.count({u, v})) continue;
        
        if (deg[u] < MAX_DEG && deg[v] < MAX_DEG) {
            edge_set.insert({u, v});
            deg[u]++; deg[v]++;
        }
    }
    
    for (auto e : edge_set) edges.push_back(e);
    return edges;
}

vector<Edge> gen_grid(int n) {
    int rows = sqrt(n);
    if (rows < 1) rows = 1;
    while (n % rows != 0) rows--; 
    int cols = n / rows;
    
    vector<Edge> edges;
    auto idx = [&](int r, int c) { return (r - 1) * cols + c; };
    
    for (int r = 1; r <= rows; ++r) {
        for (int c = 1; c <= cols; ++c) {
            int u = idx(r, c);
            if (c < cols) edges.push_back({u, idx(r, c + 1)});
            if (r < rows) edges.push_back({u, idx(r + 1, c)});
        }
    }
    return edges;
}

vector<Edge> gen_regular(int n, int k) {
    if ((n * k) % 2 != 0) k--; 
    if (k < 2) k = 2; 
    
    set<Edge> edge_set;
    vector<int> p(n);
    iota(p.begin(), p.end(), 1);
    
    int half_k = k / 2;
    for(int i = 0; i < half_k; ++i) {
        shuffle(p.begin(), p.end());
        for(int j = 0; j < n; ++j) {
            int u = p[j];
            int v = p[(j + 1) % n];
            if (u > v) swap(u, v);
            edge_set.insert({u, v});
        }
    }
    
    if (k % 2 != 0) {
        shuffle(p.begin(), p.end());
        for(int i = 0; i < n / 2; ++i) {
            int u = p[2 * i];
            int v = p[2 * i + 1];
            if (u > v) swap(u, v);
            edge_set.insert({u, v});
        }
    }
    
    vector<Edge> res;
    for(auto e : edge_set) res.push_back(e);
    return res;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    string mode = opt<string>("mode", "non");
    
    
    int t = rnd.next(1, 5);
    cout << t << "\n";
    
    for (int i = 0; i < t; ++i) {
        int n = 0;
        vector<Edge> edges;
        bool generated = false;
        
        while (!generated) {
            n = rnd.next(2, MAXN);
            string type = "random";
            
            if (mode == "adp") {
                
                int r = rnd.next(0, 3);
                if (r == 0) type = "regular";
                else if (r == 1) type = "grid";
                else if (r == 2) type = "regular_dense";
                else type = "random_dense";
            } else {
                
                int r = rnd.next(0, 4);
                if (r == 0) type = "tree";
                else if (r == 1) type = "random_sparse";
                else type = "random_dense";
            }
            
            if (type == "tree") {
                edges = gen_random_graph(n, n - 1);
            } else if (type == "grid") {
                
                if (n > 10 && rnd.next(2)) {
                    int r = rnd.next(2, (int)sqrt(n));
                    n = r * (n / r);
                }
                edges = gen_grid(n);
            } else if (type == "regular" || type == "regular_dense") {
                int k;
                if (type == "regular") {
                    int lo = 3, hi = min(6, n - 1);
                    if (lo > hi) { type = "random_sparse"; goto retry_type; }
                    k = rnd.next(lo, hi);
                } else {
                    int lo = 6, hi = min(20, n - 1);
                    if (lo > hi) { type = "random_sparse"; goto retry_type; }
                    k = rnd.next(lo, hi);
                }

                if (k > MAX_DEG) k = MAX_DEG;
                edges = gen_regular(n, k);
            } else if (type == "random_sparse") {
                retry_type:
                int m = rnd.next(n - 1, min((int)(n * 1.5), n * (n - 1) / 2));
                edges = gen_random_graph(n, m);
            } else { 
                int max_m = min(n * (n - 1) / 2, 25 * n);
                int min_m = min(n, max_m);
                int m = rnd.next(min_m, max_m);
                edges = gen_random_graph(n, m);
            }
            
            if (edges.size() >= 1 && is_connected(n, edges) && check_degrees(n, edges)) {
                generated = true;
            }
        }
        
        
        vector<int> p(n + 1);
        iota(p.begin(), p.end(), 0);
        shuffle(p.begin() + 1, p.end());
        
        vector<Edge> final_edges;
        for (auto& e : edges) {
            int u = p[e.u];
            int v = p[e.v];
            if (rnd.next(2)) swap(u, v);
            final_edges.push_back({u, v});
        }
        shuffle(final_edges.begin(), final_edges.end());
        
        int m = final_edges.size();
        int start = rnd.next(1, n);
        
        
        
        
        long long calc_base = (long long)m * 25 / 10;
        if (calc_base < 1000) calc_base = 1000;
        if (calc_base > 30000) calc_base = 30000;
        
        cout << n << " " << m << " " << start << " " << calc_base << "\n";
        for (const auto& e : final_edges) {
            cout << e.u << " " << e.v << "\n";
        }
    }
    
    return 0;
}