Codeforces Round #838 (Div. 2)——D. GCD Queries

120 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第28天,点击查看活动详情

Codeforces Round #838 (Div. 2)——D. GCD Queries

Problem - D - Codeforces

This is an interactive problem.

There is a secret permutation pp of 0,1,2,…,n−1. Your task is to find 22 indices xx and yy (1≤x,y≤n1≤x,y≤n, possibly x=yx=y) such that px=0px=0 or py=0py=0. In order to find it, you are allowed to ask at most 2n2n queries.

In one query, you give two integers ii and jj (1≤i,j≤n1≤i,j≤n, i≠ji≠j) and receive the value of gcd(pi,pj)†gcd(pi,pj)†.

Note that the permutation pp is fixed before any queries are made and does not depend on the queries.

†† gcd(x,y)gcd(x,y) denotes the greatest common divisor (GCD) of integers xx and yy. Note that gcd(x,0)=gcd(0,x)=xgcd(x,0)=gcd(0,x)=x for all positive integers xx.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤1041≤t≤104). The description of the test cases follows.

The first line of each test case contains a single integer nn (2≤n≤2⋅1042≤n≤2⋅104).

After reading the integer nn for each test case, you should begin the interaction.

It is guaranteed that the sum of nn over all test cases does not exceed 2⋅1042⋅104.

Interaction

The interaction for each test case begins by reading the integer nn.

To make a query, output "? ii jj" (1≤i,j≤n1≤i,j≤n, i≠ji≠j) without quotes. Afterwards, you should read a single integer — the answer to your query gcd(pi,pj)gcd(pi,pj). You can make at most 2n2n such queries in each test case.

If you want to print the answer, output "! xx yy" (1≤x,y≤n1≤x,y≤n) without quotes. After doing that, read 11 or −1−1. If px=0px=0 or py=0py=0, you'll receive 11, otherwise you'll receive −1−1. If you receive −1−1, your program must terminate immediately to receive a Wrong Answer verdict. Otherwise, you can get an arbitrary verdict because your solution will continue to read from a closed stream.

If you receive the integer −1−1 instead of an answer or a valid value of nn, it means your program has made an invalid query, has exceeded the limit of queries, or has given an incorrect answer on the previous test case. Your program must terminate immediately to receive a Wrong Answer verdict. Otherwise, you can get an arbitrary verdict because your solution will continue to read from a closed stream.

After printing a query or the answer, do not forget to output the end of 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;
  • flush(output) in Pascal;
  • stdout.flush() in Python;
  • see documentation for other languages.

Hacks

To hack, use the following format.

The first line should contain a single integer tt (1≤t≤1041≤t≤104).

The first line of each test case should contain a single integer nn (2≤n≤2⋅1042≤n≤2⋅104).

The second line of each test case should contain nn space separated integers p1,p2,…,pnp1,p2,…,pn. pp should be a permutation of 0,1,2,…,n−1.

The sum of nn should not exceed 2⋅1042⋅104.

Example

input

2
2
​
1
​
1
5
​
2
​
4
​
1

output

? 1 2
​
! 1 2
​
​
? 1 2
​
? 2 3
​
! 3 3

问题解析

题目是说有一个由0到n-1组成的数组,你每次可以询问两个位置的最大公约数gcd(如果有一个位置是0,那么得到的就是另一个位置的数),让你在2n次操作内找到位置0的位置。最后输出两个位置,0就在这两个位置之间。

首先,数组只由0到n-1组成,那么最大的公约数就是0和n-1得到的n-1了。

也就是说,我们尽量让gcd往大的方向增长,最后得到的两个位置就肯定有一个0.

考虑三个数a,b,c,我们询问x=gcd(a,c),y=gcd(b,c),

那么会有以下三种情况:

x>y:这时我们可以说b一定不是0。如果b是0,则y=c,而x=gcd(a,c)<=c,这就互相矛盾了。

x<y:a一定不是0,理由同上

x=y:c一定不是0,因为a!=b。 这样,每两次询问就能去掉两个位置,我们只用2n-4次询问就可以了

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>//#pragma GCC optimize(2)
//#pragma GCC optimize(3)#define endl '\n'
//#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 2e5 + 50, MOD = 998244353;
​
void solve()
{
    int n, a, b;
    cin >> n;
    int l = 1, r = 2;
    for (int i = 3; i <= n; i++)
    {
        cout << "? " << l << " " << i << endl;
        cin >> a;
        cout << "? " << r << " " << i << endl;
        cin >> b;
        if (a == b)continue;
        else if (a > b)r = i;
        else l = i;
    }
    int k;
    cout << "! " << l << " " << r << endl;
    cin >> k;
}
​
signed main()
{
   // ios_base::sync_with_stdio(false);
   // cin.tie(nullptr);
   // cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}