翻译:
简单版本和困难版本之间的唯一区别是查询数量的限制。
这是一个互动的问题。
有一个包含𝑛不同数字的数组𝑎。在一个查询中,您可以询问子段𝑎[𝑙..𝑟]中第二个最大元素的位置。在不超过20次查询中找到数组中最大元素的位置。
子段𝑎[𝑙..]𝑟]是所有的元素𝑎𝑙,𝑎𝑙+1,…,𝑎𝑟。在询问这个子段之后,你将得到这个子段在整个数组中的第二个最大值的位置。
输入
第一行包含一个整数𝑛(2≤𝑛≤105)-数组中的元素数量。
交互
您可以通过打印“?”𝑙𝑟”(1≤𝑙<𝑟≤𝑛)。答案是所有元素的第二个最大值的索引𝑎𝑙,𝑎𝑙+1,…,𝑎𝑟。数组𝑎是预先固定的,不能在交互时更改。
你可以通过打印“!”来输出答案。𝑝”,其中𝑝是数组中最大元素的索引。
您可以提出不超过20个查询。打印答案不算作查询。
打印查询后,不要忘记输出行尾并刷新输出。否则,您将得到闲置限制超过。要做到这一点,请使用:
c++中的fflush(stdout)或count .flush();
Java中的System.out.flush();
Pascal中的flush(输出);
Python中的stdout.flush();
有关其他语言,请参阅文档
黑客
要进行黑客攻击,请使用以下测试格式。
在第一行输出一个整数𝑛(2≤𝑛≤105)。在第二行中,输出𝑛整数1到𝑛的排列。𝑛在排列中的位置是最大值的位置
例子
inputCopy
5
3.
4
outputCopy
? 1 - 5
? 4个5
! 1
请注意
在示例中,假设𝑎为[5,1,4,2,3]。所以在问了[1..]5]子段4仅次于Max value,位置为3。问了[4..]5]子段2仅次于Max值,它在整个数组中的位置是4。
注意,还有其他数组𝑎会产生相同的交互,它们的答案可能不同。给出了示例输出,目的是为了理解交互作用。
思路:每次查询可以得到该位置第二小的坐标,我们第一次查询1~n,就可以得到全局第二小的值,之后我们查询1~第二小的坐标,或者第二小的坐标~n,便可以判断,最大值在哪个边界。
因为如果最大的在此区间内,返回值就会是第二小的坐标。然后我们可以根据左右区间,来不断二分查找,将第二小的坐标作为固定左端点或者固定右端点,然后不断二分即可。
感觉代码不太好写,本人快wa穿了,交互题,挺难调。
代码:
/*Looking! The blitz loop this planet to search way
Only my RAILGUN can shoot it 今すぐ
身体中を 光の速さで
駆け巡った確かな予感
掴め! 望むものなら残さず
輝ける自分らしさで
信じてるよ あの日の誓いを
この瞳に光る涙それさえも 強さになるから
*/
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<tuple>
#include<numeric>
using namespace::std;
typedef long long ll;
inline __int128 read(){
__int128 x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
int n;
int how(int x,int y){
int ff;
cout<<"? "<<x<<' '<<y<<endl;
cin>>ff;return ff;
}
int w,mid;
int main(){
ios::sync_with_stdio(false);
cin.tie(); cout.tie();
cin>>n;
if (n==1) {
cout<<"! "<<1<<endl;return 0;
}
w=-1;
int l=1,r=n,an;
an=how(l,r);
// if (n==2) {
// cout<<"! "<<3-an<<endl;return 0;
// }
if (an!=1) {
w=how(1, an);
}
// printf("%d %d\n",w,an);
if (w==an) {
r=an-1;
while (l<r) {
mid=(l+r+1)>>1;
w=how(mid, an);
if (w==an)
l=mid;
else
r=mid-1;
}
}
else{
l=an+1;
while (l<r) {
mid=(l+r)>>1;
w=how(an, mid);
if (w==an)
r=mid;
else
l=mid+1;
}
}
cout<<"! "<<l<<endl;
return 0;
}