// それぞれの数字を指数探索でやると落ちるやつ // merge sort っぽい解法の方だとちゃんと回数は抑えられるが挿入していく方だと回数が足りない #include using namespace std; int query_count = 0; bool query(const vector& q) { query_count++; cout << "? " << q.size() << " "; for(int i = 0; i < q.size(); i++) { cout << q[i] << " \n"[i==q.size()-1]; } cout << flush; string s; cin >> s; return s == "Yes"; } int n; void answer(const vector& ans) { assert(ans.size() == n); cout << "! "; for(int i = 0; i < n; i++) { cout << ans[i] << " \n"[i==n-1]; } cout << flush; } int main() { cin >> n; vector ans; vector counts(n + 1, 0); for(int i = 1; i <= n; i++) { int count = 0; // 指数探索 int ng = 1; while(query(vector(ng, i))) { count = ng; ng *= 2; if(ng > n) { ng = n + 1; break; } } // 二分探索 while(ng - count > 1) { int mid = (count + ng) / 2; if(query(vector(mid, i))) { count = mid; } else { ng = mid; } } counts[i] = count; } cerr << "[Debug] Counts: "; cerr << query_count << " queries used." << endl; for(int i = 1; i <= n; i++) { vector pos; for(int cnt = 1; cnt <= counts[i]; cnt++) { // 最後から cnt 個目の i が挿入される場所の二分探索 int ok = 0, ng = ans.size() + 1; while(ng - ok > 1) { int mid = (ok + ng) / 2; vector q; for(int j = 0; j < mid; j++) q.push_back(ans[j]); for(int j = 0; j < cnt; j++) q.push_back(i); if(query(q)) ok = mid; else ng = mid; } pos.push_back(ok); } for(auto p: pos) { ans.insert(ans.begin() + p, i); } } answer(ans); return 0; }