题目大意
有一个 n (1<=n<=2000) 个数的排列 a 和一个数 x,排列 a 和 x 全部未知,每次你可以询问下标为 i 的数a[i] 与 x 的大小关系。若该数大于 x 则回答 ' > ' 并且x 增加一;若该数小于 x 则回答'<' 并且 x 减去一;若该数等于 x 则回答 '=' 并且 x 不变。要求在 40n 次操作内确定出排列 a 。
思路
可以发现,若一直询问一个数,若该数大于x则x++,若该数小于x则x--,x将不断的逼近该数,直至出现’=‘,x等于该数。故,要将x变成指定的一个数最多需要n次询问。
当x的值等于某个数之后,让其与另一个数 y 相比,则可得到y与这个数的大小关系。再询问一次原来的那个数,x又恢复为原来的值。询问完大小之后再恢复回来需要两次操作。
故将一个数与其他所有的数比较大小需要2n次询问,加上变为指定数的n次,一共需要3n次询问。故可用3n次操作将大于和小于的数分开,类似于快速排序。每次选择集合内的某一个数,并与其他数比较,分为大于和小于两个部分,再分别递归处理。
故要跑次的指定相等和分大小,最多可以跑40n次,每次指定相等和分大小需要3n次,n<=2000,
约等于11,40/3约等于13。
code
#include<bits/stdc++.h>
#include<random>
#define int long long
using namespace std;
const int N = 2010;
int a[N];
mt19937 rnd(time(0));
//产生同unsigned int类型取值范围的随机数
//将x变为与第pos位的数相等
void hd(int pos) {
char ch;
while (1) {
cout << "? " << pos << '\n';
cout.flush();
cin >> ch;
if (ch == '=')
break;
}
}
void srt(int l, int r, vector<int>v) {
if (v.empty()) return;
if (l > r) return;
if (l == r) {
//当区间长度为1时,可确定v中所存编号所对应的数的值,即为区间端点值
for (auto k : v)
a[k] = l;
return;
}
int tmp = v[rnd() % v.size()];
//随机取一个编号,不要指定,指定的话在极端情况下会tle
hd(tmp);//让x与指定的这个编号所对应的数相等
char ch;
vector<int>s, f;//分别存大于和小于的数
for (auto k : v) {
if (k == tmp) continue;//查到选中的编号时,直接continue,询问无用
cout << "? " << k << '\n';
cout.flush();
cin >> ch;
//询问k位置上的数与指定的数的大小关系
if (ch == '>')
s.push_back(k);
else
f.push_back(k);
//将大于和小于的数分开
cout << "? " << tmp << '\n';
cout.flush();
cin >> ch;
//与指定位置的数相比,x恢复为指定位置的数
//这样才能继续判断下一位数与指定位置的数的大小关系,
//直至判断完所有数,把所有数分为大于和小于两部分
}
int p = r - s.size();
//求指定的tmp位置对应的数是谁
//r指的是目前排序的这个区间里最大的值,而动态数组s存大于tmp位置的数
//又因为目前排序的这个区间大小是连续的,
//所以最大值减去大于他的数的个数即为tmp位置所对应的值,存入a[]中作为最终答案输出
a[tmp] = p;
srt(p + 1, r, s);
srt(1, p - 1, f);
//递归处理大于和小于部分,注意每一部分对应的值都是连续的
//因为a本身是一个排列,与一个数同时比大小,也会被分成连续的大于和小于两部分
//递归直至区间长度为1,确定每一个位置所对应的值
}
void solve() {
int n = 1;
cin >> n;
vector<int>v;
for (int i = 1; i <= n; ++i) {
a[i] = 0;
v.push_back(i);//v存的是编号
}
srt(1, n, v);
cout << "! ";
for (int i = 1; i <= n; ++i)
cout << a[i] << " ";
cout << '\n';
cout.flush();//注意每一个cout都要这一句,刷新输出缓冲区
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}