交互类构造题
交互类构造题是一类偏思维的题目,其正解往往十分简单易懂,但是很难想到。
那么我们应该如何解决这样的一类问题呢?
做不出构造题?
事实:题很难构造。
它很难过!
它还有细节,
而另一个你不动脑帮忙(她最坏了)。
不可能完成的任务?
事实上,只要抓住关键点,就能极大幅度的简化和指引你的思考,当然还有可能不会
而这类题目的关键点往往集中于次数和一些特别的提示
以CF2031F为例:
题意:
交互,\(T\)组询问,每次给你一个数组长度\(n\)(保证为偶数,\(6\leq n\leq100\)),表示数据是一个\(1-n\)的重排数组(保证数据不自适应),
要求这个数组的两个中位数的下标。
你可以进行以下两个操作:
- 询问(80次):\(?\space k\space x_1\space x_2\space ……\space x_k\),其中\(x\)数组不重复且在\([1,n]\),\(k\)为偶数且\(4\leq k\leq n\),将会返回这段区间的两个中位数的值
- 输出:\(!\space i_1\space i_2\),表示答案的两个下标,顺序无所谓。
思路:
题目一看非常美妙有趣,这个询问次数很有说法
一般询问次数经常包含\(n,\log(n),\frac{n}{2}\)等等
注意到\(n\)只有\(100\),而询问有\(80\)次,\(log\)太小,\(n\)又太大,那大概率包含一个\(\frac{n}{2}\),那么还剩至少\(20\)次
注意到题目莫名其妙给个数据不自适应,这启发我们思考随机化。
接下来考虑具体做法:
首先注意中位数的性质:
若一串数的中位数为\(\frac{n}{2}\)和\(\frac{n}{2} + 1\),
那么\(1\) ~ \(\frac{n}{2}\)的个数和\(\frac{n}{2} + 1\) ~ \(n\)的个数显然恰好相等,
反之也成立。
如果我们找到一个\(1 \leq L \leq \frac{n}{2}\) 和一个 \(\frac{n}{2} + 1 \leq R \leq n\),
那么我们只需要枚举不重复的二元组,就能整出来最多4个数字,其中有2个是中位数,
直接暴力\(C_{4}^{2}\)枚举即可。
那么问题就转变为了求出符合条件的任意一组\((L, R)\),
暴力的话要进行最差\(n^2\)次的查询,显然不行
考虑随机化,注意到任意一组数它是符合条件的\((L, R)\)的概率接近于\(\frac{1}{2}\),那么我们只需要随机知道找到符合条件的\((L, R)\)即可,
这个过程的猜测次数超过\(20\)你就基本上\(100\)%猜出来了,总的猜测次数在合理范围内。
核心代码
int n;
pii query(int x, int y){
cout << "? " << n - 2 << " ";
rep(i, 1, n) if(i != x && i != y) cout << i << " ";
cout << endl;
int a, b; cin >> a >> b;
cout << endl;
return {a, b};
}
pii query(vector<int> x){
cout << "? ";
cout << x.size() << " ";
for(int i : x) cout << i << " ";
cout << endl;
int a, b; cin >> a >> b;
cout << endl;
return {a, b};
}
void out(int x, int y){
cout << "! " << x << " " << y << endl;
}
void solve(){
cin >> n;
int L = 1, R = 2;
bool f = 0;
int a = n / 2, b = n / 2 + 1;
while(1){
int i = rnd() % n + 1, j = rnd() % n + 1;
while(i == j) j = rnd() % n + 1;
pii ret = query(i, j);
if(ret.fir == a && ret.sec == b){
L = i; R = j; f = 1;
break;
}
}
set<pii> s;
rep(i, 1, n){
if(i == L || i == R) continue;
int j = i; ++j;
while(j == L || j == R) ++j;
pii ret = query({L, R, i, j});
if(ret.fir == a || ret.fir == b || ret.sec == a || ret.sec == b) s.insert({i, j});
i = j;
}
if(s.size() == 1){
pii x = (*s.begin());
out(x.fir, x.sec);
}else{
vector<int> vc;
for(pii x : s) vc.pb(x.fir), vc.pb(x.sec);
rep(i, 0, 3){
rep(j, i + 1, 3){
pii ret = query({L, R, vc[i], vc[j]});
if(ret.fir == a && ret.sec == b){
out(vc[i], vc[j]);
return ;
}
}
}
}
}

浙公网安备 33010602011771号