Description
一个1~n的排列a[1]~a[n],每次可以查询(i,j)(i!=j),返回>说明a[i]大于a[j],返回<说明a[i]小于a[j],要求在至多n+24次查询内找出a序列次大值即n-1所在位置
Input
第一行一整数n表示序列长度,对于每次查询(i,j)会输出>或<表明a[i]和a[j]的大小关系(2<=n<=2e5)
Output
输出? i j表示查询a[i]和a[j]的大小关系,输出! x表示n-1在x位置
Sample Input
5
>
>
>
>
>
<
>
Sample Output
? 1 2
? 1 3
? 1 4
? 1 5
? 2 3
? 2 4
? 4 5
! 4
Solution
类似线段树,两两比较选出最大值,n/2次查询删去了n/2个值,再n/4次查询删去n/4个值,以此类推,最多n次查询可以得到最大值,而次大值一定在logn个曾经和最大值比较的数字中,对这logn个数再依次比较即可找出次大值的位置,最多查询n+logn次,满足n+24的查询次数上限
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 222222
vector<int>g[maxn];
int n,a[maxn];
char op[3];
int main()
{
while(~scanf("%d",&n))
{
cout.flush();
for(int i=1;i<=n;i++)g[i].clear();
for(int i=1;i<=n;i++)a[i]=i;
int res=n;
while(res>1)
{
int cnt=0;
for(int i=1;i+1<=res;i+=2)
{
int j=i+1;
printf("? %d %d\n",a[i],a[j]);cout.flush();
g[a[i]].push_back(a[j]),g[a[j]].push_back(a[i]);
scanf("%s",op);cout.flush();
if(op[0]=='<')a[++cnt]=a[j];
else a[++cnt]=a[i];
}
if(res&1)a[++cnt]=a[res];
res=cnt;
}
int ans=g[a[1]][0];
for(int i=1;i<g[a[1]].size();i++)
{
printf("? %d %d\n",ans,g[a[1]][i]);cout.flush();
scanf("%s",op);cout.flush();
if(op[0]=='<')ans=g[a[1]][i];
}
printf("! %d\n",ans);cout.flush();
}
return 0;
}