Codeforces Round 967 (Div. 2) C. Guess The Tree

C. Guess The Tree

time limit per test: 2 seconds

memory limit per test: 256 megabytes

This is an interactive problem.

Misuki has chosen a secret tree with n nodes, indexed from 1 to n, and asked you to guess it by using queries of the following type:

"? a b" — Misuki will tell you which node x minimizes |d(a,x)−d(b,x)|, where d(x,y) is the distance between nodes x and y. If more than one such node exists, Misuki will tell you the one which minimizes d(a,x).

Find out the structure of Misuki's secret tree using at most 15n queries!

Input

Each test consists of multiple test cases. The first line contains a single integer t (1≤t≤200) — the number of test cases.

Each test case consists of a single line with an integer n (2≤n≤1000), the number of nodes in the tree.

It is guaranteed that the sum of n across all test cases does not exceed 1000.

Interaction

The interaction begins by reading the integer n.

Then you can make up to 15n queries.

To make a query, output a line in the format "? a b" (without quotes) (1≤a,b≤n). After each query, read an integer — the answer to your query.

To report the answer, output a line in the format "! a1 b1 a2 b2 ... an−1 bn−1" (without quotes), meaning that there is an edge between nodes ai and bi, for each 1≤i≤n−1. You can print the edges in any order.

After 15n queries have been made, the response to any other query will be −1. Once you receive such a response, terminate the program to receive the Wrong Answer verdict.

After printing each line, do not forget to output the end of line and flush the output buffer. Otherwise, you will receive the Idleness limit exceeded verdict. To flush, use:

fflush(stdout) or cout.flush() in C++;

System.out.flush() in Java;

flush(output) in Pascal;

stdout.flush() in Python;

see the documentation for other languages.

Hacks

For hacks, use the following format: The first line contains an integer t (1≤t≤200) — the number of test cases.

The first line of each test contains an integer n — the number of nodes in the hidden tree.

Then n−1 lines follow. The i-th of them contains two integers ai and bi (1≤ai,bi≤n), meaning that there is an edge between ai and bi in the hidden tree.

The sum of n over all test cases must not exceed 1000.

Example

Input

1

4

1

1

3

Output

? 1 2

? 1 3

? 1 4

! 1 2 1 3 3 4

Note

A tree is an undirected acyclic connected graph. A tree with n nodes will always have n−1 edges.

In the example case, the answer to "? 1 2" is 1. This means that there is an edge between nodes 1 and 2.

The answer to "? 1 3" is 1. This means that there is an edge between nodes 1 and 3.

The answer to "? 1 4" is 3. It can be proven that this can only happen if node 3 is connected to both node 1 and 4.

The edges of the tree are hence (1,2), (1,3) and (3,4).

【思路分析】

树二分。首先分析无向图含n-1条边,为树。我们令每次query为“? a b”,显然每次query为靠近a的二分,考虑令a为根节点,二分区间a不变,每次只变b。当且仅当query结果为a时a和b有边。由于题设条件中query次数不超过15n,我们可以直接枚举所有节点。

#include <iostream>
#include <vector>
#include <unordered_map>
#include <map>
#include <cmath>
#include <algorithm>
#include <climits>
#include <stack>
#include <cstring>
#include <iomanip>
#include <set>
#include <queue>

#define i64 long long

using namespace std;

typedef pair<i64, i64> pii;

i64 query(i64 a, i64 b) {
    i64 tmp;
    cout << "? " << a << " " << b << endl;
    cout.flush();
    cin >> tmp;
    return tmp;
}

void solve() {
    i64 n, val, k, i;
    cin >> n;
    i64 res[n];
    for (i = 1; i < n; i++) {
        val = 1;
        while (true) {
            k = val;
            val = query(i + 1, k);
            if (val == i + 1) {
                res[i] = k;
                break;
            }
        }
    }
    cout << "! ";
    for (i = 1; i < n; i++) cout << res[i] << " " << i + 1 << " ";
    cout << endl;
    cout.flush();
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值