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;
}
posted @ 2026-05-14 15:54  kzssCCC  阅读(8)  评论(0)    收藏  举报