CF2152E思路分享(交互,构造)
https://codeforces.com/problemset/problem/2152/E
题意概述
给定一个长度为 \(n^2+1\) 的排列 \(p\),找出一个长度恰好为 \(n+1\) 的单调子序列。
可以进行最多 \(n\) 次询问,每次询问一个下标集合,返回下标对应 \(p_i\) 构成序列的所有前缀最大值的下标。
\(1 \le n \le 100\),\(\sum{n^2+1} \le 10001\) 。
思路
根据鸽笼原理,这样的子序列一定存在。
考虑这样询问:初始全选,把每次询问得到的下标删掉,继续下一轮询问。
如果某一轮询问得到的序列长度 \(\gt n\),直接输出。
记 \(ord_i\) 为得到 \(i\) 位置的询问轮次。
可以发现,如果 \(ord_i\) 不为 \(1\) ,\(i\) 位置之前一定存在一个 \(j\) 满足 \(ord_j = ord_i-1\) 。因此可以根据最后的 \(ord\) 数组从后往前构造出答案。
由于只能进行 \(n\) 轮询问,需要把最后没得到的 \(ord_i\) 都标成 \(n+1\) 。如果不存在没访问的点,由鸽笼原理可知,必定存在一轮询问得到的序列长度 \(\gt n\),因此不可能。
时间复杂度 \(\mathcal{O}(n^3)\) 。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
int n;
cin >> n;
auto q = [&](vector<int>& vec){
cout << "? " << vec.size() << ' ';
for (auto& v:vec){
cout << v << ' ';
}
cout << endl;
int c;
cin >> c;
vector<int> res(c);
for (int i=0;i<c;i++){
cin >> res[i];
}
return res;
};
vector<int> ord(n*n+2,-1);
for (int tt=1;tt<=n;tt++){
vector<int> vec;
for (int i=1;i<=n*n+1;i++){
if (ord[i]==-1){
vec.push_back(i);
}
}
auto temp = q(vec);
if (temp.size()>n){
cout << "! ";
for (int i=0;i<n+1;i++){
cout << temp[i] << ' ';
}
cout << endl;
return;
}
for (auto& v:temp){
ord[v] = tt;
}
}
for (int i=1;i<=n*n+1;i++){
if (ord[i]==-1){
ord[i] = n+1;
}
}
vector<int> res;
int j = n+1;
for (int i=n*n+1;i>=1;i--){
if (ord[i]==j){
j--;
res.push_back(i);
}
}
reverse(res.begin(),res.end());
cout << "! ";
for (int i=0;i<n+1;i++){
cout << res[i] << ' ';
}
cout << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号