Problem: icpc2024_northern_eurasia_finals_h
Difficulty: medium
Categories: greedy, search
Time limit: 6000 ms, memory limit: 1024 MB

This is an interactive problem.

Harry and Hermione are trying to hunt down hoglins which are haunting Hogwarts. There is a long hallway in Hogwarts, consisting of $n$ individual cells, numbered from 1 to $n$ from the left to the right.

Hermione can cast a spell that would block any cell of the hallway of her choosing. After the spell is cast, the blocked cell will remain blocked while she casts other spells.

Hoglins are simple creatures; all they do is randomly move around and bump into stuff. To be more precise, every hoglin has a range which it considers to be accessible. Initially, when the hoglin appears, it is a range from the cell 1 to the cell $n$ .

Initially, a single hoglin appears in a cell of the hallway chosen uniformly at random. Then, until this hoglin is caught, the following happens on every round of the hunt:

• Hermione can cast a spell to block any single cell of her choosing, or do nothing.   
• If the cell she is trying to block is the cell with a hoglin in it, the hoglin is caught. After that, all the blocked cells become free again, and, if there are more hoglins to be caught, a new hoglin immediately appears in a random location, and the hunt begins again.   
• Otherwise, the hoglin chooses a cell uniformly at random from its accessible range and tries to move to that cell, moving one cell at a time towards a chosen cell. Regardless of the distance, all the steps of the movement, as described below, happen in the same round.   
• If the chosen cell is to the right of the hoglin, it moves to the right; if the chosen cell is to the left of the hoglin, it moves to the left. If the chosen cell is the same as where the hoglin is now, it does nothing.   
• If at any point during the movement towards the chosen cell a hoglin is trying to move to the right or to the left from an unblocked cell at position $i$ to the neighbouring blocked cell at position $i \pm 1$ , the hoglin updates the right or left boundary of its accessible range correspondingly to be $i$ .   
• If on the way to the chosen cell, the hoglin tries to move to a blocked cell, Harry and Hermione hear a loud sound, as the hoglin bumps into the blocked cell. In this case, the hoglin returns to the position it has originally started from at the beginning of this round.   
• Otherwise, if the hoglin does not bump into any blocked cells on its way, it does not change its accessible range and stays at the new position. In that case, Harry and Hermione hear nothing.

To free Hogwarts from hoglins, Harry and Hermione should catch $k$ of them, but they don’t have much time. They can only afford to hunt hoglins for at most 200 000 rounds. Please help them find an efficient strategy to do that.

# Interaction Protocol

First, the testing system will write two integers $n$ and $k$ ( $1 \leq n \leq 1 0 ^ { 1 8 } ; 1 \leq k \leq 8 0 0 _ { \cdot }$ ) — the number of cells in Hogwarts’ hallway and the number of hoglins that should be caught. Then the catching process begins.

The following interaction proceeds in rounds as described in the problem statement.

At the start of each round, your program should output Hermione’s action — an integer $p$ ( $0 \leq p \leq n$ ) representing the position of the cell Hermione is going to block. If $p = 0$ or the cell at position $p$ is already blocked, she does nothing in this round.

Then, if the current position of the hoglin is at the newly blocked cell $p$ , the hoglin is caught; the testing system outputs 2, all the blocked cells become free, and interaction rounds start again. In case you caught the $k$ -th hoglin, the testing system outputs 3 instead of 2, and your program should immediately stop execution.

Otherwise, the hoglin attempts to move according to the rules described in the problem statement. If in the process it bumps into any blocked cell, the testing system outputs 1; otherwise, it outputs 0.

If your 200 000-th action does not catch the $k$ -th hoglin, the testing system outputs -1 instead of its usual answer, and your program should immediately stop execution to guarantee the “Wrong Answer” verdict.

The interactor in this problem is not adaptive. It is guaranteed that the hoglins follow the rules described in the problem statement. The starting cell for each hoglin is chosen uniformly at random and their moves are chosen uniformly at random from the range of cells that they consider accessible.

The problem has at most 15 tests.

Here is the summary of all possible interactor answers:

• -1 — too many actions;   
• 0 — hoglin moved successfully, did not bump;   
• 1 — hoglin attempted to move, bumped into blocked cell;   
• 2 — hoglin is caught, interaction starts again;   
• 3 — hoglin is caught, stop.

# Example

<table><tr><td>standard input</td><td>standard output</td><td colspan="9">Illustration</td></tr><tr><td colspan="2" rowspan="2">92 3</td><td rowspan="2"></td><td></td><td></td><td>123456789</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>start at 8</td></tr><tr><td colspan="2" rowspan="2">7</td><td rowspan="8"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 3</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 4, success</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 7</td></tr><tr><td>5</td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 9, bumped</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 5</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 3,bumped</td></tr><tr><td colspan="2">1 0 9</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 1</td></tr><tr><td colspan="2" rowspan="2">1</td><td rowspan="2"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td rowspan="2">move to 4, success</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td>block 9</td></tr><tr><td colspan="2">4</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 5, bumped</td></tr><tr><td colspan="2" rowspan="2">2</td><td rowspan="2"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 4, caught</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>start at 3</td></tr><tr><td colspan="2">5 7</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 5</td></tr><tr><td colspan="2" rowspan="2">1 0</td><td rowspan="2"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 9,bumped</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>block 7</td></tr><tr><td colspan="2">0</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 1, success</td></tr><tr><td colspan="2" rowspan="2">0</td><td rowspan="2"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>do nothing</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>move to 2, success</td></tr><tr><td colspan="2">2</td><td colspan="10">block 2,caught</td></tr></table>

# Note

We show the sample from the point of view of the hoglins.

The black dot shows the current position of the hoglin.

Crosses mark blocked cells.

White cells mark the range which the hoglin considers to be accessible; other cells are marked gray.

On the right is the action that was performed by either Hermione or the hoglin to get to this state from the previous one.

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

using namespace std;


static long long queries = 0;
static long long query_limit = 200000;



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

    
    
    long long n = inf.readLong();
    int k = inf.readInt();
    long long seed = inf.readLong();
    
    
    rnd.setSeed(seed);

    
    cout << n << " " << k << endl;

    
    long long pos = rnd.next(1LL, n); 
    long long L = 1;                  
    long long R = n;
    set<long long> blocked;           
    int caught = 0;                   

    
    while (true) {
        
        if (queries > 0 && queries % 100 == 0) {
            log_metrics();
        }

        
        string token;
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF (expected move)");
        }

        long long p;
        
        if (token == "?" || token == "!") {
            if (!(cin >> p)) {
                finish(_wa, "Unexpected EOF after prefix token");
            }
        } else {
            try {
                size_t idx;
                p = stoll(token, &idx);
                if (idx != token.size()) finish(_wa, "Garbage in input token");
            } catch (...) {
                finish(_wa, "Invalid integer format: " + token);
            }
        }

        
        if (p < 0 || p > n) {
            finish(_wa, "Action out of bounds: " + to_string(p));
        }

        queries++;

        
        bool just_caught = false;
        
        
        
        if (p != 0 && blocked.find(p) == blocked.end()) {
            
            if (p == pos) {
                just_caught = true;
            } else {
                blocked.insert(p);
            }
        }

        if (just_caught) {
            caught++;
            blocked.clear(); 
            
            if (caught == k) {
                
                cout << 3 << endl;
                finish(_ok, "Caught all " + to_string(k) + " hoglins");
            } else {
                
                
                
                
                if (queries >= query_limit) {
                    cout << -1 << endl;
                    finish(_wa, "Query limit reached immediately after catching a hoglin (caught " + to_string(caught) + "/" + to_string(k) + ")");
                }

                
                cout << 2 << endl;
                
                
                pos = rnd.next(1LL, n);
                L = 1;
                R = n;
                
                continue; 
            }
        }

        
        
        if (queries >= query_limit) {
            cout << -1 << endl;
            finish(_wa, "Query limit exceeded without catching K-th hoglin");
        }

        
        long long target = rnd.next(L, R);
        
        if (target == pos) {
            
            cout << 0 << endl;
        } else {
            
            long long dir = (target > pos) ? 1 : -1;
            
            
            
            long long obstacle = -1;
            
            if (dir == 1) {
                
                auto it = blocked.upper_bound(pos);
                if (it != blocked.end()) {
                    long long blk = *it;
                    if (blk <= target) { 
                        obstacle = blk;
                    }
                }
            } else {
                
                auto it = blocked.lower_bound(pos);
                if (it != blocked.begin()) {
                    it--;
                    long long blk = *it;
                    if (blk >= target) { 
                        obstacle = blk;
                    }
                }
            }

            if (obstacle != -1) {
                
                cout << 1 << endl;
                
                
                
                if (dir == 1) {
                    
                    
                    R = obstacle - 1;
                } else {
                    
                    
                    L = obstacle + 1;
                }
                
                
            } else {
                
                cout << 0 << endl;
                pos = target;
            }
        }
    }

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

    
    
    long long seed_input = -1;
    bool explicit_case = false;

    if (argc > 1) {
        string s = argv[1];
        try {
            size_t pos;
            long long val = stoll(s, &pos);
            
            if (pos == s.size() && val >= 1 && val <= 20) {
                seed_input = val;
                explicit_case = true;
            }
        } catch (...) {
            
            explicit_case = false;
        }
    }

    long long n;
    int k;

    if (explicit_case) {
        switch (seed_input) {
            case 1: n = 1; k = 1; break;                   
            case 2: n = 2; k = 1; break;                   
            case 3: n = 10; k = 1; break;
            case 4: n = 100; k = 50; break;
            case 5: n = 1000000000000000000LL; k = 1; break; 
            case 6: n = 1000000000000000000LL; k = 800; break; 
            case 7: n = 2; k = 800; break;                 
            case 8: n = 1000; k = 800; break;              
            case 9: n = 1000000; k = 100; break;
            case 10: n = 999999999999999999LL; k = 799; break; 
            default:
                
                n = rnd.next(100LL, 100000LL);
                k = rnd.next(1, 100);
                break;
        }
    } else {
        
        int type_n = rnd.next(100);
        
        
        if (type_n < 5) {
            
            n = rnd.next(1LL, 100LL);
        } else if (type_n < 15) {
            
            n = rnd.next(100LL, 100000LL);
        } else if (type_n < 30) {
            
            n = rnd.next(100000LL, 1000000000LL);
        } else if (type_n < 50) {
            
            n = rnd.next(1000000000LL, 1000000000000000LL);
        } else {
            
            n = rnd.next(1000000000000000LL, 1000000000000000000LL);
        }

        
        int type_k = rnd.next(100);
        if (type_k < 10) {
            k = 1;
        } else if (type_k < 30) {
            k = rnd.next(1, 10);
        } else if (type_k < 60) {
            k = rnd.next(10, 100);
        } else {
            
            k = rnd.next(100, 800);
        }
    }

    
    cout << n << " " << k << "\n";

    
    if (mode == "non") {
        
        
        long long interactor_seed = rnd.next(1LL, 2000000000000000000LL); 
        cout << interactor_seed << "\n";
    } 
    else if (mode == "adp") {
        
        
    }

    return 0;
}