主要思路:构造。
思路
方法一
一个一个的找,分别查询 \([1,n],[n+1,2n],\dots,[n(n-1)+1,n^{2}]\) 中最快的人,再把 \(n\) 个人合起来查询,不过很明显的是,这个方法很蠢,并不能切掉此题。
方法二
找第二快的人,只有最快的人在的一组需重新询问,剩下答案无需改变。需排除的人的一组用不是现在任何一组最大值的东西来补空位。这样询问总次数达到 \(2n^{2}-n+1\)。
方法三
明显的方法而仍与题目要求差 \(n\)。
当只剩下 \(2n\) 个元素的时候,如果是恰好每组两个,那么这时候删除 \(\max\) 就不需要再进行一次维护,仅剩的一个元素直接继承 \(\max\)。
在补充元素的时候,并不真的把元素补进去,而是在求 \(\max\) 的时候直接顺着往后找满 \(n\) 个,然后当删除的组不平衡(极差大于 \(1\))以后,真的将元素最多的组里的元素补给最少的,这样就能保证一直平衡。
这样询问次数就到达了 \(2n^{2}-2n+1\)。
AC Code
#include<bits/stdc++.h>
using namespace std;
const int N = 405;
int n, ans[N], vis[405];
set<int>s[N];
vector<int>asn;
inline int ask(set<int>&s) {
cout << "? ";
for (auto x : s)cout << x << " ";
cout << "\n";
int ret;
cin >> ret;
return ret;
}
inline get(set<int>&s) {
int tot = 1;
for (int i = n - s.size(); i; i--) {
while (vis[tot] || s.find(tot) != s.end())tot++;
s.insert(tot);
}
}
inline void solve() {
cin >> n;
for (int i = 0; i <= n * n + 1; i++)vis[i] = 0, ans[i] = 0, s[i].clear();
asn.clear();
for (int i = 1; i <= n; i++) {
for (int j = (i - 1) * n + 1; j <= i * n; j++)s[i].insert(j);
ans[i] = ask(s[i]), vis[ans[i]] = 1;
}
for (int tot = n * n; tot >= 2 * n; tot--) {
set<int>now;
for (int i = 1; i <= n; i++)now.insert(ans[i]);
get(now);
int x = ask(now), id = 0;
for (int i = 1; i <= n; i++)if (ans[i] == x)id = i;
asn.push_back(x);
s[id].erase(x);
get(s[id]);
ans[id] = ask(s[id]);
vis[ans[id]] = 1;
for (int i = 1; i <= n; i++)if (i != id)s[i].erase(ans[id]);
}
set<int>now;
for (int i = 1; i <= n; i++)now.insert(ans[i]);
get(now);
for (int tot = n - 1; tot; tot--) {
int x = ask(now);
now.erase(x);
get(now);
asn.push_back(x);
}
for (auto x : now)if (vis[x])asn.push_back(x);
cout << "! ";
for (auto x : asn)cout << x << " ";
cout << endl;
}
int t;
int main() {
cin >> t;
while (t--) solve();
return 0;
}
标签:CF1896G,int,题解,元素,Pepe,tot,--,max,2n
From: https://www.cnblogs.com/AUBSwords/p/18346595