Problem: cf1639_i
Difficulty: medium
Categories: graph, greedy, 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 std::mt19937 rng(1337); 

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 MapCase {
    int n, m, start, bmc;
    vector<pair<int,int>> edges;
};

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

    
    int T = inf.readInt();
    vector<MapCase> maps(T);
    long long total_limit = 0;

    for(int i = 0; i < T; ++i) {
        maps[i].n = inf.readInt();
        maps[i].m = inf.readInt();
        maps[i].start = inf.readInt();
        maps[i].bmc = inf.readInt();
        
        total_limit += (2LL * maps[i].bmc);
        for(int j = 0; j < maps[i].m; ++j) {
            int u = inf.readInt();
            int v = inf.readInt();
            maps[i].edges.push_back({u, v});
        }
    }
    
    
    query_limit = total_limit;
    log_metrics();

    
    cout << T << endl;

    
    for(int i = 0; i < T; ++i) {
        const auto& map_data = maps[i];
        int n = map_data.n;
        int m = map_data.m;
        int start_node = map_data.start;
        int bmc = map_data.bmc;
        
        
        vector<vector<int>> adj(n + 1);
        vector<int> deg(n + 1, 0);
        for(const auto& e : map_data.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_node << " " << bmc << endl;
        for(const auto& e : map_data.edges) {
            cout << e.first << " " << e.second << endl;
        }
        cout.flush();

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

        long long moves = 0;
        long long map_limit = 2LL * bmc;

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

            
            
            if (moves > map_limit) {
                cout << "F" << endl;
                break; 
            }

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

            
            cout << "R " << d;
            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 > d) {
                finish(_wa, "Invalid move index " + to_string(choice) + " (degree is " + to_string(d) + ")");
            }
            
            
            queries++;
            
            if (queries % 50 == 0) log_metrics();

            curr = neighbors[choice - 1]; 
            if (!visited[curr]) {
                visited[curr] = true;
                visited_count++;
            }
            moves++;
        }
    }
    
    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(4242);


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

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

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

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

        
        
        long long map_limit = 2LL * base_move_count;
        query_limit += map_limit;

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

        
        if (t == 0) log_metrics();

        
        int curr = start_node;
        vector<bool> visited(n + 1, false);
        visited[curr] = true;
        int visited_count = 1;
        long long moves_this_map = 0;
        
        
        while (true) {
            
            if (visited_count == n) {
                cout << "AC" << endl;
                break;
            }

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

            
            struct NeighborOpt {
                int real_u;
                int d;
                int f; 
            };
            
            vector<NeighborOpt> options;
            options.reserve(adj[curr].size());
            for (int v : adj[curr]) {
                options.push_back({v, deg[v], visited[v] ? 1 : 0});
            }

            
            
            shuffle(options.begin(), options.end(), rng);

            
            
            cout << "R " << options.size();
            for (const auto& opt : options) {
                cout << " " << opt.d << " " << opt.f;
            }
            cout << endl; 

            
            int choice_idx;
            if (!(cin >> choice_idx)) {
                finish(_pe, "Unexpected EOF or invalid format reading choice");
            }

            
            if (choice_idx < 1 || choice_idx > (int)options.size()) {
                finish(_wa, format("Invalid choice index %d, expected 1..%d", choice_idx, (int)options.size()));
            }

            
            
            
            int next_v = options[choice_idx - 1].real_u;

            
            curr = next_v;
            if (!visited[curr]) {
                visited[curr] = true;
                visited_count++;
            }
            moves_this_map++;
            queries++;

            
            if (queries % 200 == 0) log_metrics();
            
            
            if (queries > 2000000) {
                finish(_pe, "Hard query limit exceeded (potential infinite loop)");
            }
        }
    }

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


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


typedef pair<int,int> Edge;


bool hasEdge(int u, int v, const set<Edge>& edges) {
    if (u > v) swap(u, v);
    return edges.count({u, v});
}



bool tryAddEdge(int u, int v, set<Edge>& edges, vector<int>& deg) {
    if (u == v) return false;
    if (u > v) swap(u, v);
    if (edges.count({u, v})) return false;
    
    if (deg[u] >= 50 || deg[v] >= 50) return false;
    
    edges.insert({u, v});
    deg[u]++;
    deg[v]++;
    return true;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    
    string mode = opt<string>("mode", "non");
    long long seedVal = atoll(argv[1]);
    
    
    
    int T = 1;
    cout << T << endl;

    int n = 0;
    int start_node = 1;
    vector<Edge> final_edges;

    
    if (seedVal >= 1 && seedVal <= 5) {
        if (seedVal == 1) { 
            n = 5;
            for(int i=1; i<n; ++i) final_edges.push_back({i, i+1});
        } else if (seedVal == 2) { 
            n = 6;
            for(int i=2; i<=n; ++i) final_edges.push_back({1, i});
        } else if (seedVal == 3) { 
            n = 10;
            for(int i=1; i<n; ++i) final_edges.push_back({i, i+1});
            final_edges.push_back({n, 1});
        } else if (seedVal == 4) { 
            n = 7;
            for(int i=1; i<=n; ++i)
                for(int j=i+1; j<=n; ++j) final_edges.push_back({i, j});
        } else { 
            n = 15;
            for(int i=2; i<=n; ++i) final_edges.push_back({i/2, i});
        }
        start_node = 1;
    } else {
        
        n = rnd.next(20, 300); 
        
        
        enum Type { RANDOM, TREE, CATERPILLAR, REGULAR, GRID, DENSE_CORE };
        Type type;

        if (mode == "adp") {
            
            
            int r = rnd.next(1, 100);
            if (r <= 50) type = REGULAR;
            else if (r <= 80) type = GRID;
            else type = DENSE_CORE;
        } else {
            
            
            int r = rnd.next(1, 100);
            if (r <= 40) type = RANDOM;
            else if (r <= 70) type = TREE;
            else type = CATERPILLAR;
        }

        set<Edge> edges;
        vector<int> deg(n + 1, 0);

        if (type == RANDOM) {
            
            for (int i = 2; i <= n; ++i) {
                tryAddEdge(i, rnd.next(1, i - 1), edges, deg);
            }
            
            int limit = min((long long)25 * n, (long long)n * (n - 1) / 2);
            int extra = rnd.next(0, min(5 * n, limit - (int)edges.size()));
            for (int i = 0; i < extra; ++i) {
                tryAddEdge(rnd.next(1, n), rnd.next(1, n), edges, deg);
            }
        } else if (type == TREE) {
            for (int i = 2; i <= n; ++i) {
                int p = rnd.next(1, i - 1);
                
                int tries = 0;
                while (deg[p] >= 50 && tries < 50) {
                    p = rnd.next(1, i - 1);
                    tries++;
                }
                if (deg[p] < 50) tryAddEdge(i, p, edges, deg);
                else {
                    
                    for(int k=1; k<i; ++k) if(deg[k] < 50) { tryAddEdge(i, k, edges, deg); break; }
                }
            }
        } else if (type == CATERPILLAR) {
            
            int len = rnd.next(n / 3, n - 2);
            for (int i = 1; i < len; ++i) tryAddEdge(i, i + 1, edges, deg);
            
            for (int i = len + 1; i <= n; ++i) {
                int p = rnd.next(1, len);
                if (deg[p] < 50) tryAddEdge(i, p, edges, deg);
                else {
                    
                    for(int k=1; k<=len; ++k) if(deg[k] < 50) { tryAddEdge(i, k, edges, deg); break; }
                }
            }
        } else if (type == REGULAR) {
            
            int k = rnd.next(3, 10); 
            if (k % 2 != 0 && n % 2 != 0) k++; 
            
            
            vector<int> offsets;
            for (int i = 1; i <= k / 2; ++i) offsets.push_back(i);
            if (k % 2 != 0) offsets.push_back(n / 2);

            for (int i = 1; i <= n; ++i) {
                for (int off : offsets) {
                    int j = (i + off - 1) % n + 1;
                    tryAddEdge(i, j, edges, deg);
                }
            }
            
            
            vector<Edge> temp(edges.begin(), edges.end());
            int swaps = n * 5;
            for (int i = 0; i < swaps && temp.size() > 2; ++i) {
                int idx1 = rnd.next(0, (int)temp.size() - 1);
                int idx2 = rnd.next(0, (int)temp.size() - 1);
                if (idx1 == idx2) continue;
                int u1 = temp[idx1].first, v1 = temp[idx1].second;
                int u2 = temp[idx2].first, v2 = temp[idx2].second;
                
                
                if (deg[u1] > 50 || deg[u2] > 50 || deg[v1] > 50 || deg[v2] > 50) continue; 
                
                
                if (hasEdge(u1, u2, edges) || hasEdge(v1, v2, edges)) continue;
                if (u1 == u2 || v1 == v2) continue; 
                set<int> distinct = {u1, v1, u2, v2};
                if (distinct.size() != 4) continue;

                
                edges.erase({min(u1, v1), max(u1, v1)});
                edges.erase({min(u2, v2), max(u2, v2)});
                edges.insert({min(u1, u2), max(u1, u2)});
                edges.insert({min(v1, v2), max(v1, v2)});
                temp[idx1] = {min(u1, u2), max(u1, u2)};
                temp[idx2] = {min(v1, v2), max(v1, v2)};
            }
        } else if (type == GRID) {
            int w = (int)sqrt(n);
            if (w < 2) w = 2;
            int h = n / w;
            auto idx = [&](int r, int c) { return (r - 1) * w + c; };
            for (int r = 1; r <= h; ++r) {
                for (int c = 1; c <= w; ++c) {
                    int u = idx(r, c);
                    if (c < w) tryAddEdge(u, idx(r, c + 1), edges, deg);
                    if (r < h) tryAddEdge(u, idx(r + 1, c), edges, deg);
                }
            }
            
            int used = h * w;
            for (int i = used + 1; i <= n; ++i) {
                int p = rnd.next(1, used); 
                if (deg[p] < 4) tryAddEdge(i, p, edges, deg);
                else {
                    for(int k=1; k<=used; ++k) if(deg[k]<50) { tryAddEdge(i, k, edges, deg); break; }
                }
                used++; 
            }
        } else { 
            
            for (int i = 1; i < n; ++i) tryAddEdge(i, i + 1, edges, deg);
            tryAddEdge(n, 1, edges, deg);
            int target_m = min((long long)n * 4, (long long)25 * n);
            int fails = 0;
            while ((int)edges.size() < target_m && fails < 200) {
                if (!tryAddEdge(rnd.next(1, n), rnd.next(1, n), edges, deg)) fails++;
            }
        }

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

        for (auto e : edges) {
            final_edges.push_back({p[e.first - 1], p[e.second - 1]});
        }
        
        start_node = rnd.next(1, n);
    }
    
    
    if (final_edges.empty()) {
        
        final_edges.push_back({1, 2});
        if (n < 2) n = 2;
    }

    int m = final_edges.size();
    
    
    
    
    
    
    long long bmc_calc = 1000 + 4LL * n + m / 2;
    int bmc = (int)max(1000LL, min(30000LL, bmc_calc));

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

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

    return 0;
}