Problem: cf2181_c
Difficulty: hard
Categories: search, math
Time limit: 3000 ms, memory limit: 1024 MB

Ivan and Petr like to play with cacti — special graphs where each edge belongs to at most one simple cycle, and the graph is connected. Multiple edges between pairs of vertices and loops are allowed.

They invent the following game:

* Petr secretly builds a cactus with $n$ vertices and $m$ edges. The edges are labeled from $1$ to $m$.
* Petr only tells Ivan the number $m$.
* Ivan is then allowed to ask questions of the following form:
  + He chooses a subset $S$ of edge labels (see below about limitations on the subset), and asks: "If we only keep the edges whose labels are in $S$ (and all $n$ vertices), is the resulting graph connected?"
  + Petr must answer either "yes" or "no".

After asking at most $8m$ questions, Ivan must determine, for every edge:

1. whether this edge lies on some cycle in the cactus;
2. if it does, what is the length of that simple cycle.

In this problem, each loop is considered a simple cycle of length $1$ and two edges between the same pair of vertices form a simple cycle of length $2$.

However, Ivan is still very young and only knows numbers up to $14$. So:

* if an edge lies on a simple cycle of length at most $14$, he must output that exact length;
* if an edge lies on a simple cycle of length greater than $14$, he must say that this edge lies on a big cycle.

Also, to avoid having to list a lot of edges each time, Ivan always asks about an edge set obtained from the set used in one of the previous queries, or from the set of all edges, by removing exactly one edge.

Can you design a strategy that allows Ivan to complete this task?

Input

Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 100$). The description of the test cases follows.

The first line of each test case contains $m$ ($1 \le m \le 10^4$) — the number of edges in the cactus.

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

Interaction

To ask a query, output a line in the following format (without the quotes):

* "? $p$ $e$" ($0 \le p \le q$; $1 \le e \le m$), where $p$ is the number of a previous query or zero (queries are numbered from 1 in the order they are asked), $q$ is the number of queries made before the current one, $e$ is the label an edge.

The query denotes a graph consisting of the edges used in the query number $p$ (or all edges if $p = 0$), with edge $e$ removed. The interactor checks whether this graph is connected when considering all original vertices and returns a single integer:

* $1$ if the graph denoted by the query is connected;
* $0$ if the graph denoted by the query is not connected;
* $-1$ if you have exceeded $8m$ allowed queries or edge $e$ was already removed from the set of edges used in query $p$. In this case you should terminate your program to receive a Wrong Answer verdict.

When you have found the answer, output a single line in the following format:

* "! $e_1$ $e_2$ $\dots$ $e_m$" ($-1 \le e_i \le 14$ for all $i$),

where:

* if $e_i$ is positive, then edge number $i$ belongs to a simple cycle of length exactly $e_i$,
* if $e_i = 0$, then edge number $i$ belongs to a big cycle (simple cycle of length greater than $14$),
* if $e_i = -1$, then edge number $i$ does not belong to any cycle.

The interactor returns a single integer:

* $1$ if your answer is correct. Proceed to the next test case or terminate your program if this was the last case.
* $-1$ if your answer is incorrect. In this case you should terminate your program to receive a Wrong Answer verdict.

The interactor is not adaptive, meaning that the graph is fixed before you ask any queries.

Example

Input

```
1
7

0

1

1

1
```

Output

```
? 0 1

? 0 3

? 2 4

! -1 3 1 3 2 3 2
```

Note

In the example interaction, the input and output contain empty lines to align interactor responses with queries. These empty lines do not appear in the actual input and output.

In this example, the graph has $5$ vertices and $7$ edges; edges $1$ through $7$, in this order, are $(1, 2)$, $(2, 3)$, $(3, 3)$, $(3, 4)$, $(4, 5)$, $(2, 4)$, $(4, 5)$.

 ![](https://espresso.codeforces.com/8a8b0f0f4dbcd28c3bb58009be11598464b67f35.png)

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 void log_metrics() {
    try {
        tout << "queries=" << queries << '\n';
        tout << "query_limit=" << query_limit << '\n';
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct PST {
    struct Node {
        int l = 0, r = 0;
        int mx = 0;
    };
    vector<Node> tr;

    void reset(size_t reserveNodes) {
        tr.clear();
        tr.reserve(reserveNodes);
        tr.push_back(Node{}); 
    }

    int mx(int v) const { return v == 0 ? 0 : tr[v].mx; }

    int addPoint(int v, int tl, int trr, int pos, int delta) {
        int nv = (int)tr.size();
        tr.push_back(v == 0 ? Node{} : tr[v]);
        if (tl == trr) {
            tr[nv].mx = (v == 0 ? 0 : tr[v].mx) + delta;
            return nv;
        }
        int tm = (tl + trr) >> 1;
        if (pos <= tm) tr[nv].l = addPoint(v == 0 ? 0 : tr[v].l, tl, tm, pos, delta);
        else tr[nv].r = addPoint(v == 0 ? 0 : tr[v].r, tm + 1, trr, pos, delta);
        tr[nv].mx = max(mx(tr[nv].l), mx(tr[nv].r));
        return nv;
    }

    int getPoint(int v, int tl, int trr, int pos) const {
        if (v == 0) return 0;
        if (tl == trr) return tr[v].mx;
        int tm = (tl + trr) >> 1;
        if (pos <= tm) return getPoint(tr[v].l, tl, tm, pos);
        return getPoint(tr[v].r, tm + 1, trr, pos);
    }
};

struct CaseData {
    int n = 0, m = 0;
    vector<int> u, v;            
    vector<char> isLoop;         
    vector<char> isBridge;       
    vector<int> cycleOfEdge;     
    vector<int> cycleLen;        
    vector<int> expected;        
    int numCycles = 0;
};

static CaseData readAndPrecomputeOneCase() {
    CaseData cd;
    cd.n = inf.readInt(1, 20000, "n");
    cd.m = inf.readInt(1, 10000, "m");
    cd.u.assign(cd.m + 1, 0);
    cd.v.assign(cd.m + 1, 0);
    cd.isLoop.assign(cd.m + 1, 0);
    cd.isBridge.assign(cd.m + 1, 0);
    cd.cycleOfEdge.assign(cd.m + 1, 0);

    for (int i = 1; i <= cd.m; i++) {
        cd.u[i] = inf.readInt(1, cd.n, "u");
        cd.v[i] = inf.readInt(1, cd.n, "v");
        cd.isLoop[i] = (cd.u[i] == cd.v[i]);
    }

    vector<vector<pair<int,int>>> adj(cd.n + 1);
    adj.assign(cd.n + 1, {});
    for (int i = 1; i <= cd.m; i++) {
        if (cd.isLoop[i]) continue;
        int a = cd.u[i], b = cd.v[i];
        adj[a].push_back({b, i});
        adj[b].push_back({a, i});
    }

    
    cd.cycleLen.assign(1, 0);
    cd.numCycles = 0;
    for (int i = 1; i <= cd.m; i++) {
        if (cd.isLoop[i]) {
            cd.numCycles++;
            cd.cycleLen.push_back(1);
            cd.cycleOfEdge[i] = cd.numCycles;
        }
    }

    
    vector<int> tin(cd.n + 1, 0), low(cd.n + 1, 0), parent(cd.n + 1, 0), parentEdge(cd.n + 1, 0);
    int timer = 0;

    struct Frame {
        int u, p, pe, it;
    };

    auto assignCycleFromBackEdge = [&](int from, int toAncestor, int backEid) {
        vector<int> edges;
        edges.push_back(backEid);
        int cur = from;
        while (cur != toAncestor) {
            int pe = parentEdge[cur];
            if (pe == 0) {
                finish(_fail, "Internal error: back-edge path escaped to root (invalid cactus input?)");
            }
            edges.push_back(pe);
            cur = parent[cur];
        }
        cd.numCycles++;
        int cid = cd.numCycles;
        cd.cycleLen.push_back((int)edges.size());
        for (int eid : edges) {
            if (cd.cycleOfEdge[eid] != 0) {
                finish(_fail, "Invalid cactus: edge belongs to multiple cycles");
            }
            cd.cycleOfEdge[eid] = cid;
        }
    };

    for (int start = 1; start <= cd.n; start++) {
        if (tin[start] != 0) continue;

        vector<Frame> st;
        st.push_back({start, 0, 0, 0});
        parent[start] = 0;
        parentEdge[start] = 0;
        tin[start] = low[start] = ++timer;

        while (!st.empty()) {
            Frame &f = st.back();
            if (f.it >= (int)adj[f.u].size()) {
                int u = f.u;
                int p = f.p;
                int pe = f.pe;
                st.pop_back();
                if (p != 0) {
                    low[p] = min(low[p], low[u]);
                    if (low[u] > tin[p]) {
                        cd.isBridge[pe] = 1;
                    }
                }
                continue;
            }

            auto [to, eid] = adj[f.u][f.it++];
            if (eid == f.pe) continue;

            if (tin[to] == 0) {
                parent[to] = f.u;
                parentEdge[to] = eid;
                tin[to] = low[to] = ++timer;
                st.push_back({to, f.u, eid, 0});
            } else {
                low[f.u] = min(low[f.u], tin[to]);
                if (tin[to] < tin[f.u]) {
                    assignCycleFromBackEdge(f.u, to, eid);
                }
            }
        }
    }

    cd.expected.assign(cd.m + 1, -1);
    for (int i = 1; i <= cd.m; i++) {
        if (cd.isLoop[i]) {
            cd.expected[i] = 1;
            continue;
        }
        if (cd.isBridge[i]) {
            cd.expected[i] = -1;
            continue;
        }
        int cid = cd.cycleOfEdge[i];
        if (cid <= 0 || cid > cd.numCycles) {
            finish(_fail, "Invalid cactus: non-bridge edge without a cycle id");
        }
        int len = cd.cycleLen[cid];
        cd.expected[i] = (len <= 14 ? len : 0);
    }

    return cd;
}

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

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = inf.readInt(1, 100, "t");
    vector<CaseData> cases;
    cases.reserve(t);

    long long sumLimit = 0;
    for (int i = 0; i < t; i++) {
        CaseData cd = readAndPrecomputeOneCase();
        sumLimit += 8LL * cd.m;
        cases.push_back(std::move(cd));
    }

    query_limit = sumLimit;
    log_metrics(); 

    long long hard_cap = max(200000LL, 10LL * query_limit);

    cout << t << '\n';
    cout.flush();

    for (int tc = 0; tc < t; tc++) {
        const CaseData& cd = cases[tc];
        int m = cd.m;

        cout << m << '\n';
        cout.flush();

        struct QState {
            int rootRemoved = 0; 
            int rootCycle = 0;   
            int removedBridges = 0;
        };

        vector<QState> qs;
        qs.reserve((size_t)min<long long>(hard_cap, 8LL * m + 5));
        qs.push_back(QState{}); 

        PST pstRemoved, pstCycle;
        size_t reserveNodes = (size_t)(min<long long>(hard_cap, 8LL * m + 5) + 5) * 20;
        pstRemoved.reset(reserveNodes);
        pstCycle.reset(reserveNodes);

        int q = 0;
        while (true) {
            string cmd;
            if (!(cin >> cmd)) {
                finish(_pe, "Unexpected EOF from contestant");
            }

            if (cmd == "?") {
                long long pLL, eLL;
                if (!(cin >> pLL >> eLL)) {
                    finish(_pe, "Malformed query line");
                }

                queries++;
                if (queries > hard_cap) {
                    finish(_pe, "Hard cap exceeded");
                }

                if (pLL < 0 || pLL > q) {
                    cout << -1 << '\n';
                    cout.flush();
                    finish(_pe, "Query refers to invalid previous query index");
                }
                if (eLL < 1 || eLL > m) {
                    cout << -1 << '\n';
                    cout.flush();
                    finish(_pe, "Query edge index out of range");
                }

                int p = (int)pLL;
                int e = (int)eLL;

                int baseRemoved = qs[p].rootRemoved;
                int baseCycle = qs[p].rootCycle;
                int baseBridges = qs[p].removedBridges;

                if (p != 0) {
                    int already = pstRemoved.getPoint(baseRemoved, 1, m, e);
                    if (already != 0) {
                        cout << -1 << '\n';
                        cout.flush();
                        finish(_pe, "Edge already removed from referenced set");
                    }
                }

                QState cur;
                cur.removedBridges = baseBridges + (cd.isBridge[e] ? 1 : 0);

                cur.rootRemoved = pstRemoved.addPoint(baseRemoved, 1, m, e, 1);

                cur.rootCycle = baseCycle;
                int cid = cd.cycleOfEdge[e];
                if (cid != 0) {
                    cur.rootCycle = pstCycle.addPoint(baseCycle, 1, cd.numCycles, cid, 1);
                }

                qs.push_back(cur);
                q++;

                int cycleMax = (cur.rootCycle == 0 ? 0 : pstCycle.tr[cur.rootCycle].mx);
                int connected = (cur.removedBridges == 0 && cycleMax <= 1) ? 1 : 0;

                cout << connected << '\n';
                cout.flush();
            } else if (cmd == "!") {
                vector<int> ans(m + 1, 0);
                for (int i = 1; i <= m; i++) {
                    long long x;
                    if (!(cin >> x)) {
                        finish(_pe, "Malformed answer: not enough integers");
                    }
                    if (x < -1 || x > 14) {
                        cout << -1 << '\n';
                        cout.flush();
                        finish(_pe, "Answer value out of allowed range");
                    }
                    ans[i] = (int)x;
                }

                bool ok = true;
                for (int i = 1; i <= m; i++) {
                    if (ans[i] != cd.expected[i]) {
                        ok = false;
                        break;
                    }
                }

                if (!ok) {
                    cout << -1 << '\n';
                    cout.flush();
                    finish(_wa, "Wrong final answer");
                }

                cout << 1 << '\n';
                cout.flush();
                break;
            } else {
                finish(_pe, "Unknown command token");
            }
        }
    }

    finish(_ok, "OK");
    return 0;
}


Adaptive interactor (C++):

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

struct Edge {
    int u, v;
};

struct CactusBuilder {
    int n = 1;
    vector<Edge> edges;

    int newVertex() { return ++n; }

    void addEdge(int u, int v) { edges.push_back({u, v}); }

    int addBridge(int from) {
        int to = newVertex();
        addEdge(from, to);
        return to;
    }

    void addLoop(int v) { addEdge(v, v); }

    void addTwoCycle(int v) {
        int to = newVertex();
        addEdge(v, to);
        addEdge(v, to);
    }

    void addCycle(int attach, int len) {
        if (len <= 0) return;
        if (len == 1) {
            addLoop(attach);
            return;
        }
        if (len == 2) {
            addTwoCycle(attach);
            return;
        }
        int first = newVertex();
        addEdge(attach, first);
        int prev = first;
        for (int i = 0; i < len - 2; i++) {
            int nxt = newVertex();
            addEdge(prev, nxt);
            prev = nxt;
        }
        addEdge(prev, attach);
    }
};

static void appendSmallRemainder(vector<int>& parts, int r) {
    
    if (r <= 0) return;
    if (r % 3 == 0) {
        parts.insert(parts.end(), r / 3, 3);
    } else if (r % 3 == 1) {
        if (r >= 4) {
            parts.push_back(2);
            parts.push_back(2);
            parts.insert(parts.end(), (r - 4) / 3, 3);
        } else {
            parts.push_back(1);
        }
    } else { 
        parts.push_back(2);
        parts.insert(parts.end(), (r - 2) / 3, 3);
    }
}

static vector<int> makePartsMostly1415(int totalEdges) {
    vector<int> parts;
    int r = totalEdges;

    while (r >= 29) {
        if (rnd.next(0, 1) == 0) {
            parts.push_back(15);
            parts.push_back(14);
        } else {
            parts.push_back(14);
            parts.push_back(15);
        }
        r -= 29;
    }
    if (r >= 15) {
        parts.push_back(15);
        r -= 15;
    }
    if (r >= 14) {
        parts.push_back(14);
        r -= 14;
    }
    appendSmallRemainder(parts, r);

    int sum = 0;
    for (int x : parts) sum += x;
    ensure(sum == totalEdges);
    return parts;
}

static void buildCornerSeed1(CactusBuilder& b, int m) {
    
    if (m <= 0) m = 1;
    if (m == 1) {
        b.addLoop(1);
        return;
    }
    
    int cur = 1;
    for (int i = 0; i < m - 1; i++) cur = b.addBridge(cur);
    b.addLoop(1);
}

static void buildCornerSeed2(CactusBuilder& b, int m) {
    
    
    if (m < 8) {
        int cur = 1;
        for (int i = 0; i < max(0, m - 1); i++) cur = b.addBridge(cur);
        if ((int)b.edges.size() < m) b.addLoop(1);
        while ((int)b.edges.size() < m) b.addLoop(1);
        return;
    }

    int used = 0;

    int cur = 1;
    vector<int> spine;
    spine.push_back(1);

    int spineLen = min(5, m - 7);
    for (int i = 0; i < spineLen; i++) {
        cur = b.addBridge(cur);
        spine.push_back(cur);
        used++;
    }

    auto addBlock = [&](int attach, int len) {
        b.addCycle(attach, len);
        used += len;
    };

    addBlock(spine[min(2, (int)spine.size() - 1)], 1);
    addBlock(spine[min(3, (int)spine.size() - 1)], 2);
    addBlock(spine[min(4, (int)spine.size() - 1)], 3);
    addBlock(spine.back(), 14);
    addBlock(spine[min(1, (int)spine.size() - 1)], 15);

    int remaining = m - used;
    if (remaining < 0) {
        
        b = CactusBuilder();
        buildCornerSeed1(b, m);
        return;
    }

    
    while (remaining >= 15) {
        addBlock(1, 15);
        remaining -= 15;
    }
    while (remaining >= 14) {
        addBlock(1, 14);
        remaining -= 14;
    }
    vector<int> tail;
    appendSmallRemainder(tail, remaining);
    for (int len : tail) addBlock(1, len);

    ensure((int)b.edges.size() == m);
}

static void buildHard(CactusBuilder& b, int m, int biasToRootPct) {
    if (m <= 0) m = 1;
    if (m == 1) {
        b.addLoop(1);
        return;
    }

    int spineLen = min(300, max(1, m / 60));
    spineLen = min(spineLen, m - 1); 
    vector<int> spine;
    spine.reserve(spineLen + 1);

    int cur = 1;
    spine.push_back(cur);
    for (int i = 0; i < spineLen; i++) {
        cur = b.addBridge(cur);
        spine.push_back(cur);
    }

    int remaining = m - spineLen;
    if (remaining == 0) {
        ensure((int)b.edges.size() == m);
        return;
    }

    
    vector<int> parts = makePartsMostly1415(remaining);

    for (int len : parts) {
        int attach;
        if (rnd.next(0, 99) < biasToRootPct) {
            attach = 1;
        } else {
            attach = spine[rnd.next(0, (int)spine.size() - 1)];
        }
        b.addCycle(attach, len);
    }

    ensure((int)b.edges.size() == m);
}

int main(int argc, char** argv) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int mOpt = opt<int>("m", -1);

    long long seedVal = 0;
    if (argc >= 2) {
        
        seedVal = atoll(argv[1]);
    }

    int m;
    if (mOpt != -1) {
        m = mOpt;
    } else if (seedVal == 1) {
        m = 1;
    } else if (seedVal == 2) {
        m = 60;
    } else if (seedVal == 3) {
        m = 10000;
    } else {
        m = rnd.next(9000, 10000);
    }
    m = max(1, min(10000, m));

    if (mode == "adp") {
        
        
        cout << 1 << "\n";
        cout << m << "\n";
        return 0;
    }

    if (mode != "non") mode = "non";

    CactusBuilder b;
    if (seedVal == 1) {
        buildCornerSeed1(b, m);
    } else if (seedVal == 2) {
        buildCornerSeed2(b, m);
    } else if (seedVal == 3) {
        buildHard(b, m, 85);
    } else {
        int bias = 70 + rnd.next(0, 20); 
        buildHard(b, m, bias);
    }

    ensure((int)b.edges.size() == m);
    cout << 1 << "\n";
    cout << b.n << " " << m << "\n";
    for (const auto& e : b.edges) {
        cout << e.u << " " << e.v << "\n";
    }
    return 0;
}