Problem: cf2022_d2
Difficulty: medium
Categories: greedy, math
Time limit: 2500 ms, memory limit: 256 MB

This is the hard version of the problem. In this version, you must use the minimum number of queries possible.

This is an interactive problem.

It is a tradition in Mexico's national IOI trainings to play the game "Asesino", which is similar to "Among Us" or "Mafia".

Today, $n$ players, numbered from $1$ to $n$, will play "Asesino" with the following three roles:

* Knight: a Knight is someone who always tells the truth.
* Knave: a Knave is someone who always lies.
* Impostor: an Impostor is someone everybody thinks is a Knight, but is secretly a Knave.

Each player will be assigned a role in the game. There will be exactly one Impostor but there can be any (possible zero) number of Knights and Knaves.

As the game moderator, you have accidentally forgotten the roles of everyone, but you need to determine the player who is the Impostor.

To determine the Impostor, you will ask some questions. In each question, you will pick two players $i$ and $j$ ($1 \leq i, j \leq n$; $i \neq j$) and ask if player $i$ thinks that player $j$ is a Knight. The results of the question is shown in the table below.

|  |  |  |  |
| --- | --- | --- | --- |
|  | Knight | Knave | Impostor |
| Knight | Yes | No | Yes |
| Knave | No | Yes | No |
| Impostor | No | Yes | — |

The response of the cell in row $a$ and column $b$ is the result of asking a question when $i$ has role $a$ and $j$ has row $b$. For example, the "Yes" in the top right cell belongs to row "Knight" and column "Impostor", so it is the response when $i$ is a Knight and $j$ is an Impostor. 

Find the Impostor in the minimum number of queries possible. That is, let $f(n)$ be the minimum integer such that for $n$ players, there exists a strategy that can determine the Impostor using at most $f(n)$ questions. Then, you should use at most $f(n)$ questions to determine the Impostor.

Note: the grader is adaptive: the roles of the players are not fixed in the beginning and may change depending on your questions. However, it is guaranteed that there exists an assignment of roles that is consistent with all previously asked questions under the constraints of this problem.

Input

The first line of input contains a single integer $t$ ($1 \leq t \leq 10^3$) — the number of test cases. The description of the test cases follows.

The first line of each test case contains a single integer $n$ ($3 \le n \le 10^5$) — the number of people playing the game.

It is guaranteed that the sum of $n$ over all test cases does not exceed $10^5$.

Interaction

To ask a question, output a line in the following format:

* "? i j" ($1 \leq i,j \leq n$; $i \neq j$) — to ask player $i$ if they think player $j$ is a Knight.

The jury will output a "1" if player $i$ thinks player $j$ is a Knight, and "0" otherwise.

When you have determined which player the Impostor is, output a line in the following format:

* "! i" ($1 \leq i \leq n$) — the Impostor is player $i$.

Note that answering does not count to your limit of $f(n)$ questions.

If you have made an invalid output, used more than $f(n)$ questions or wrongly determined the Impostor, the jury will respond with "-1" and you will receive a Wrong Answer verdict. Upon receiving "-1", your program must terminate immediately. Otherwise, you may receive an arbitrary verdict because your solution might be reading from a closed stream.

After printing a query do not forget to output the end of the line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

* fflush(stdout) or cout.flush() in C++;
* System.out.flush() in Java;
* sys.stdout.flush() in Python;
* std::io::stdout().flush() in Rust;
* see the documentation for other languages.

Example

Input

```
2
7

1

0

0

1

1

0

0

4

0

1

1

1
```

Output

```
? 1 3

? 7 6

? 2 5

? 6 2

? 4 5

? 4 6

? 1 4

! 4

? 1 2

? 2 3

? 3 4

? 4 1

! 3
```

Note

Note that the example test cases do not represent an optimal strategy for asking questions and are only shown for the sake of demonstrating the interaction format. Specifically, we cannot determine which player is the Impostor from the questions asked in the examples.

In the first test case of the example, players at indices $2$ and $6$ are Knights, players at indices $1$, $3$, $5$, and $7$ are Knaves, and the Impostor is at index $4$. The following is an explanation of the questions asked:

* In the first query, player $i$ is a Knave and player $j$ is a Knave. The answer is "yes" since Knaves always lie.
* In the second query, player $i$ is a Knave and player $j$ is a Knight. The answer is "no" since Knaves always lie.
* In the third query, player $i$ is a Knight and player $j$ is a Knave. The answer is "no" since Knights always tell the truth.
* In the fourth query, player $i$ is a Knight and player $j$ is a Knight. The answer is "yes" since Knights always tell the truth.
* In the fifth query, player $i$ is a Impostor and player $j$ is a Knave. The answer is "yes" since the Impostor always lies.
* In the sixth query, player $i$ is a Impostor and player $j$ is a Knight. The answer is "no" since the Impostor always lies.
* In the seventh query, player $i$ is a Knave and player $j$ is a Impostor. The answer is "no" since Knaves always lie and Knaves thinks that the Impostor is a Knight.

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 const long long HARD_CAP = 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());
}

static void reply_minus_one_and_wa(const string& msg) {
    try {
        cout << -1 << endl;
        cout.flush();
    } catch (...) {
        
    }
    finish(_wa, msg);
}

static long long f_min_queries(int n) {
    
    
    
    
    if (n == 3) return 4;
    return n;
}





bool is_perceived_knight(int role) {
    return (role == 1 || role == 2);
}

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

    struct TestCase {
        int n = 0;
        vector<int> roles; 
        int impostor = -1;
        long long limit = 0;
    };

    int t = inf.readInt(1, 1000, "t");
    vector<TestCase> tests;
    tests.reserve(t);

    for (int tc = 0; tc < t; tc++) {
        TestCase cur;
        cur.n = inf.readInt(3, 100000, "n");
        cur.roles.assign(cur.n + 1, 0);
        for (int i = 1; i <= cur.n; i++) {
            cur.roles[i] = inf.readInt(0, 2, "role");
            if (cur.roles[i] == 2) cur.impostor = i;
        }
        if (cur.impostor == -1) finish(_fail, "Hidden input: missing impostor");
        cur.limit = f_min_queries(cur.n);
        tests.push_back(std::move(cur));
        query_limit += tests.back().limit;
    }

    log_metrics(); 

    cout << t << endl;
    cout.flush();

    for (int tc = 0; tc < t; tc++) {
        const TestCase& cur = tests[tc];
        const int n = cur.n;
        const long long limit = cur.limit;

        cout << n << endl;
        cout.flush();

        long long used = 0;
        while (true) {
            if (queries > HARD_CAP) finish(_pe, "Hard query limit exceeded");

            string cmd;
            if (!(cin >> cmd)) finish(_wa, "Unexpected EOF (no answer provided)");

            if (cmd == "!") {
                int ans = 0;
                if (!(cin >> ans)) finish(_pe, "Expected impostor index after '!'");
                if (ans < 1 || ans > n) reply_minus_one_and_wa("Answer out of range");
                if (ans != cur.impostor) {
                    reply_minus_one_and_wa(format("Wrong answer: expected %d, got %d", cur.impostor, ans));
                }
                break; 
            }

            if (cmd != "?") {
                reply_minus_one_and_wa("Invalid query format (expected '? i j' or '! i')");
            }

            int u = 0, v = 0;
            if (!(cin >> u >> v)) finish(_pe, "Expected two integers after '?'");
            if (u < 1 || u > n || v < 1 || v > n || u == v) {
                reply_minus_one_and_wa("Invalid query arguments");
            }

            used++;
            queries++;
            if (queries % 10 == 0) log_metrics();

            if (used > limit) {
                reply_minus_one_and_wa("Query limit exceeded");
            }

            bool perceived = is_perceived_knight(cur.roles[v]);
            bool response = (cur.roles[u] == 1) ? perceived : !perceived;
            cout << (response ? 1 : 0) << endl;
            cout.flush();
        }
    }

    finish(_ok, "OK");
}


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

using namespace std;


static long long total_queries = 0;
static long long query_limit = 0;


static void log_metrics() {
    try {
        tout << "queries=" << total_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 void reply_minus_one_and_wa(const string& msg) {
    try {
        cout << -1 << endl;
        cout.flush();
    } catch (...) {
        
    }
    finish(_wa, msg);
}

static long long f_min_queries(int n) {
    
    
    
    
    if (n == 3) return 4;
    return n;
}




struct DSU {
    vector<int> parent;
    vector<int> diff; 

    DSU(int n) {
        parent.resize(n + 1);
        iota(parent.begin(), parent.end(), 0);
        diff.assign(n + 1, 0);
    }

    pair<int, int> find(int i) {
        if (parent[i] == i) return {i, 0};
        auto root = find(parent[i]);
        parent[i] = root.first;
        diff[i] = diff[i] ^ root.second;
        return {parent[i], diff[i]};
    }

    
    
    bool unite(int i, int j, int weight) {
        auto root_i = find(i);
        auto root_j = find(j);
        if (root_i.first != root_j.first) {
            parent[root_i.first] = root_j.first;
            diff[root_i.first] = weight ^ root_i.second ^ root_j.second;
            return true;
        }
        return false;
    }

    int get_path_xor(int i, int j) {
        auto root_i = find(i);
        auto root_j = find(j);
        if (root_i.first != root_j.first) return 0; 
        return root_i.second ^ root_j.second;
    }
};


struct Edge {
    int to;
    bool is_out; 
};




void get_flow_through(int n, int u, int v, const vector<vector<Edge>>& adj, vector<int>& F_nodes) {
    queue<pair<int, int>> q;
    q.push({v, 0});
    
    vector<int> parent(n + 1, 0);
    
    
    
    vector<bool> edge_dir_at_child(n + 1); 
    vector<bool> edge_dir_at_parent(n + 1); 
    vector<bool> visited(n + 1, false);
    
    visited[v] = true;
    bool found = false;
    
    while(!q.empty()){
        auto [curr, par] = q.front(); q.pop();
        if(curr == u) { found = true; break; }
        for(auto& e : adj[curr]){
            if(e.to == par) continue;
            if(!visited[e.to]){
                visited[e.to] = true;
                parent[e.to] = curr;
                
                
                
                
                
                
                
                
                edge_dir_at_parent[e.to] = e.is_out;
                edge_dir_at_child[e.to] = !e.is_out;
                q.push({e.to, curr});
            }
        }
    }
    
    if (!found) return; 

    
    
    vector<int> path;
    int curr = u;
    while(curr != v) {
        path.push_back(curr);
        curr = parent[curr];
    }
    path.push_back(v);
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    if (!edge_dir_at_child[u]) F_nodes.push_back(u);
    
    
    
    
    
    
    
    if (path.size() > 1) {
        int c = path[path.size() - 2];
        if (edge_dir_at_parent[c]) F_nodes.push_back(v);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    for (size_t i = 1; i < path.size() - 1; ++i) {
        int node = path[i];
        int child = path[i-1];
        if (edge_dir_at_parent[child] != edge_dir_at_child[node]) {
            F_nodes.push_back(node);
        }
    }
}

struct CaseData {
    int n;
};

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

    
    vector<CaseData> cases;
    int T = inf.readInt(1, 1000, "t");
    
    for (int i = 0; i < T; ++i) {
        int n = inf.readInt(3, 100000, "n");
        
        for (int j = 0; j < n; ++j) (void)inf.readInt();
        cases.push_back({n});
        query_limit += f_min_queries(n);
    }
    
    log_metrics(); 

    
    cout << T << endl;
    cout.flush();

    for (const auto& tc : cases) {
        int n = tc.n;
        const long long per_limit = f_min_queries(n);
        long long used = 0;

        cout << n << endl; 
        cout.flush();
        
        
        DSU dsu(n);
        vector<vector<Edge>> tree_adj(n + 1);
        bitset<100005> candidates;
        for(int i=1; i<=n; ++i) candidates.set(i);
        int valid_count = n;

        bool solved = false;
        while (!solved) {
            string type;
            if (!(cin >> type)) finish(_wa, "Unexpected EOF");
            
            if (type == "!") {
                int ans;
                if (!(cin >> ans)) finish(_wa, "Unexpected EOF after !");
                
                if (ans < 1 || ans > n) reply_minus_one_and_wa("Answer out of range");
                
                if (!candidates.test(ans)) {
                    reply_minus_one_and_wa("Incorrect impostor identified");
                }
                
                
                if (valid_count > 1) {
                    reply_minus_one_and_wa("Ambiguous answer: multiple candidates remain valid");
                }
                
                
                solved = true;
                
            } else {
                
                
                int u, v;
                if (type != "?") {
                    reply_minus_one_and_wa("Invalid query format (expected '? i j')");
                }
                if (!(cin >> u >> v)) finish(_wa, "Unexpected EOF reading query args");
                
                total_queries++;
                
                if (total_queries % 1000 == 0) log_metrics();
                
                used++;
                if (used > per_limit) {
                    reply_minus_one_and_wa("Query limit exceeded");
                }
                
                if (u < 1 || u > n || v < 1 || v > n || u == v) {
                    reply_minus_one_and_wa("Invalid query arguments");
                }
                
                
                if (dsu.find(u).first != dsu.find(v).first) {
                    
                    int ans = 0; 
                    dsu.unite(u, v, ans ^ 1);
                    
                    
                    tree_adj[u].push_back({v, true});
                    tree_adj[v].push_back({u, false});
                    
                    cout << ans << endl;
                } else {
                    
                    int path_xor = dsu.get_path_xor(u, v);
                    int ans_normal = path_xor ^ 1;
                    int ans_conflict = 1 - ans_normal;
                    
                    
                    vector<int> F_nodes;
                    get_flow_through(n, u, v, tree_adj, F_nodes);
                    
                    
                    int valid_in_F = 0;
                    for (int x : F_nodes) {
                        if (candidates.test(x)) valid_in_F++;
                    }
                    
                    int size_A = valid_count - valid_in_F; 
                    int size_B = valid_in_F;              
                    
                    if (size_A >= size_B) {
                        
                        for (int x : F_nodes) {
                            if (candidates.test(x)) {
                                candidates.reset(x);
                                valid_count--;
                            }
                        }
                        cout << ans_normal << endl;
                    } else {
                        
                        
                        vector<int> survivors;
                        for(int x : F_nodes) {
                            if(candidates.test(x)) survivors.push_back(x);
                        }
                        candidates.reset();
                        for(int x : survivors) candidates.set(x);
                        valid_count = survivors.size();
                        cout << ans_conflict << endl;
                    }
                }
                cout.flush();
            }
        }
    }
    
    finish(_ok, "OK");
    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 n = opt<int>("n", -1);

    
    if (n == -1) {
        int p = rnd.next(100);
        if (p < 5) n = 3;                              
        else if (p < 10) n = rnd.next(4, 10);          
        else if (p < 20) n = rnd.next(11, 100);        
        else if (p < 35) n = rnd.next(101, 2000);      
        else if (p < 95) n = rnd.next(2001, 100000);   
        else n = 100000;                               
    }

    
    println(1);
    
    println(n);

    if (mode == "adp") {
        
        
        vector<int> a(n, -1);
        println(a);
    } else {
        
        
        vector<int> a(n);
        int impostor_idx = rnd.next(n);

        
        int type = rnd.next(5);
        
        
        
        
        
        
        for (int i = 0; i < n; i++) {
            if (i == impostor_idx) {
                a[i] = 2;
            } else {
                int role = 0;
                if (type == 0) role = 1;
                else if (type == 1) role = 0;
                else if (type == 2) role = (rnd.next(10) == 0 ? 0 : 1);
                else if (type == 3) role = (rnd.next(10) == 0 ? 1 : 0);
                else role = rnd.next(0, 1);
                a[i] = role;
            }
        }
        println(a);
    }

    return 0;
}