这是一道交互题
给定正整数 (n),交互库有长为 (n) 的排列 (p),你至多可以询问 (2n+420) 次三个不同正整数 (a,b,c),交互库告诉你 ({|p_a-p_b|,|p_b-p_c|,|p_c-p_a|}) 的中位数。(T) 组数据。
(nge 20),(sum nle 10^5),(p_1<p_2),non-adaptive。
看到这个魔怔限制就知道肯定是二合一(分步做)
首先可以不管值的对称性,只要先做一个答案然后 (p_1>p_2) 的时候令 (p_i:=n+1-p_i) 即可。
知道了 (1,2) 的位置之后遍历一遍就做完了。
知道了 (|p_a-p_b|le (n-4)/3) 的 (a,b) 之后遍历一遍就找到了 (1,2) 的位置。
暴力随一些 (a,b,c) 直到交互库返回值 (le (n-4)/6) 即可。
#include<bits/stdc++.h>
#define PB emplace_back
using namespace std;
const int N = 100003;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int T, n, m, a, b, p[N], mx;
vector<int> E[N];
#ifdef NTFOrz
int ans[N];
#endif
int ask(int x, int y, int z){
printf("? %d %d %d
", x, y, z);
#ifdef NTFOrz
int a[3] = {abs(ans[x] - ans[y]), abs(ans[y] - ans[z]), abs(ans[z] - ans[x])};
sort(a, a + 3); printf("%d
", a[1]); return a[1];
#else
fflush(stdout);
int _; read(_); return _;
#endif
}
void solve(){
read(n); m = (n-4)/6; mx = 0;
#ifdef NTFOrz
for(int i = 1;i <= n;++ i) read(ans[i]);
#endif
for(int i = 0;i < n;++ i) E[i].resize(0);
while(true){
int c = rng() % n + 1;
a = rng() % n + 1;
b = rng() % n + 1;
if(a != b && b != c && c != a && ask(a, b, c) <= m) break;
}
for(int i = 1;i <= n;++ i)
if(i != a && i != b){
int v = ask(i, a, b);
chmax(mx, v); E[v].PB(i);
}
if(E[mx-1].size() > 1 && ask(E[mx-1][0], E[mx][0], a) > ask(E[mx-1][1], E[mx][0], a))
swap(E[mx-1][0], E[mx-1][1]);
p[a = E[mx][0]] = 1;
p[b = E[mx-1][0]] = 2;
for(int i = 1;i <= n;++ i)
if(i != a && i != b)
p[i] = ask(i, a, b) + 2;
if(p[1] > p[2])
for(int i = 1;i <= n;++ i)
p[i] = n + 1 - p[i];
putchar('!');
for(int i = 1;i <= n;++ i){
printf(" %d", p[i]);
#ifdef NTFOrz
assert(p[i] == ans[i]);
#endif
}
putchar('
');
fflush(stdout);
read(n);
}
int main(){read(T); while(T --) solve();}