Problem: cf1639_a
Difficulty: easy
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 global_queries = 0;
static long long global_query_limit = 0;


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


template <typename... Args>
void finish(TResult verdict, const char* fmt, Args... args) {
    log_metrics();
    quitf(verdict, fmt, args...);
}


int parse_int(const string& s) {
    try {
        size_t pos;
        int val = stoi(s, &pos);
        if (pos != s.size()) return -1;
        return val;
    } catch (...) {
        return -1;
    }
}

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

    
    
    mt19937 rng(589214);

    
    int t = inf.readInt();
    
    cout << t << endl;

    for (int ti = 0; ti < t; ti++) {
        
        int n = inf.readInt();
        int m = inf.readInt();
        int start_node = inf.readInt();
        int base_move_count = inf.readInt();

        
        
        
        long long current_limit = 2LL * base_move_count;
        global_query_limit += current_limit;
        
        
        if (ti == 0) log_metrics();

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

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

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

        
        int current_u = start_node;
        vector<bool> visited(n + 1, false);
        visited[start_node] = true;
        int visited_count = 1;
        int moves = 0;

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

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

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

            
            int choice;
            if (!(cin >> choice)) {
                finish(_wa, "Unexpected EOF while reading move");
            }

            
            if (choice < 1 || choice > (int)neighbors.size()) {
                finish(_wa, "Move index %d out of range [1, %d]", choice, (int)neighbors.size());
            }

            
            int next_u = neighbors[choice - 1];
            current_u = next_u;
            moves++;
            global_queries++;
            
            
            if (global_queries % 50 == 0) log_metrics();

            if (!visited[current_u]) {
                visited[current_u] = true;
                visited_count++;
            }
        }
    }

    
    finish(_ok, "Processed %d maps", t);
    return 0;
}


Adaptive interactor (C++):



















#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


static long long total_queries = 0;
static long long total_query_limit = 0;

static void log_metrics() {
    
    
    try {
        tout << "queries=" << total_queries << endl;
        tout << "query_limit=" << total_query_limit << endl;
    } catch (...) {}
}


static 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);

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

    
    
    std::mt19937 rng(5489u); 

    bool all_maps_ok = true;

    for (int ti = 0; ti < t; ++ti) {
        
        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");

        
        long long current_limit = 2LL * base_move_count;
        total_query_limit += current_limit;
        
        
        log_metrics();

        
        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 << endl;
        }
        
        cout.flush();

        
        vector<bool> visited(n + 1, false);
        int visited_count = 0;
        int current_u = start;
        
        
        if (!visited[current_u]) {
            visited[current_u] = true;
            visited_count++;
        }

        long long moves = 0;
        bool map_solved = false;
        
        
        long long hard_cap = current_limit + 2000; 

        
        while (true) {
            
            if (visited_count == n) {
                map_solved = true;
                break;
            }
            
            if (moves > current_limit) {
                break; 
            }
            if (moves > hard_cap) {
                 finish(_pe, "Solution exceeded hard move cap (infinite loop?)");
            }

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

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

            
            int choice;
            if (!(cin >> choice)) {
                
                finish(_wa, "Unexpected EOF or invalid input from solution");
            }
            
            
            if (choice < 1 || choice > (int)neighbors.size()) {
                finish(_wa, format("Invalid neighbor index %d, expected 1..%d", choice, (int)neighbors.size()));
            }

            
            current_u = neighbors[choice - 1];
            moves++;
            total_queries++;
            
            
            if (moves % 500 == 0) log_metrics();

            if (!visited[current_u]) {
                visited[current_u] = true;
                visited_count++;
            }
        }

        if (map_solved) {
            cout << "AC" << endl;
        } else {
            cout << "F" << endl;
            
            all_maps_ok = false;
        }
    }

    if (!all_maps_ok) {
        finish(_wa, "Failed to traverse at least one map within move limit");
    } else {
        finish(_ok, "All maps traversed successfully");
    }

    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");

    
    
    int t = 1;
    println(t);

    for (int ti = 0; ti < t; ti++) {
        int n;
        if (mode == "non") {
            
            n = rnd.next(2, 300);
        } else {
            
            n = rnd.next(200, 300);
        }

        
        
        
        
        

        vector<set<int>> adj(n);
        vector<int> deg(n, 0);

        
        auto try_add_edge = [&](int u, int v) {
            if (u == v) return false;
            if (adj[u].count(v)) return false;
            
            if (deg[u] >= 50 || deg[v] >= 50) return false;
            
            adj[u].insert(v);
            adj[v].insert(u);
            deg[u]++;
            deg[v]++;
            return true;
        };

        if (mode == "non") {
            
            
            
            vector<int> p(n);
            iota(p.begin(), p.end(), 0);
            shuffle(p.begin(), p.end());

            for (int i = 1; i < n; i++) {
                
                int parent = p[rnd.next(0, i - 1)];
                
                
                if (deg[parent] >= 50) {
                    for (int k = 0; k < i; k++) {
                        if (deg[p[k]] < 50) {
                            parent = p[k];
                            break;
                        }
                    }
                }
                
                if (deg[parent] < 50) {
                    try_add_edge(p[i], parent);
                }
            }

            
            long long max_m = min((long long)n * (n - 1) / 2, (long long)25 * n);
            
            int target_m = rnd.next(n - 1, (int)max_m);
            
            
            int current_m = 0;
            for(int i=0; i<n; ++i) current_m += deg[i];
            current_m /= 2;

            int fails = 0;
            while (current_m < target_m && fails < 1000) {
                int u = rnd.next(0, n - 1);
                int v = rnd.next(0, n - 1);
                if (try_add_edge(u, v)) {
                    current_m++;
                    fails = 0;
                } else {
                    fails++;
                }
            }

        } else {
            
            
            
            
            int k = rnd.next(3, 48); 

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

            for (int i = 0; i < n; i++) {
                try_add_edge(p[i], p[(i + 1) % n]);
            }

            
            
            vector<int> candidates(n);
            iota(candidates.begin(), candidates.end(), 0);
            
            int fails = 0;
            
            while (fails < 2000 && candidates.size() >= 2) {
                
                int idx1 = rnd.next(0, (int)candidates.size() - 1);
                int u = candidates[idx1];
                if (deg[u] >= k) {
                    candidates[idx1] = candidates.back();
                    candidates.pop_back();
                    continue;
                }

                
                int idx2 = rnd.next(0, (int)candidates.size() - 1);
                int v = candidates[idx2];
                if (u == v) continue;
                if (deg[v] >= k) {
                    candidates[idx2] = candidates.back();
                    candidates.pop_back();
                    continue;
                }

                if (try_add_edge(u, v)) {
                    fails = 0;
                } else {
                    fails++;
                }
            }
        }

        
        vector<pair<int, int>> edges;
        for (int u = 0; u < n; u++) {
            for (int v : adj[u]) {
                if (u < v) {
                    edges.push_back({u + 1, v + 1}); 
                }
            }
        }

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

        
        int start = rnd.next(1, n);
        
        
        int base_move_count = rnd.next(1000, 3000);

        
        println(n, (int)edges.size(), start, base_move_count);
        for (const auto& e : edges) {
            println(e.first, e.second);
        }
    }

    return 0;
}