【CF809B】Glad to see you!(二分交互题)
大致题意: 交互题。已知在一个长度为\(n\)的序列中选中了\(k\)个位置,你每次可以询问两个位置\(x,y\),则若\(|x-a|\le |y-b|\)(\(a,b\)为\(k\)个被选中的位置中分别离\(x,y\)最近的位置),返回\(TAK\),否则返回\(NIE\)。你需要在\(60\)次询问内求出任意两个被选中的位置并输出。(其实k根本没啥用)
前言
猛然惊觉叶老师给的题单里面居然还有交互题。
交互题这么有趣,不写白不写,白写谁不写?
二分
\(60\)次询问,一看就是\(log\)级别的,一下就能想到二分。而且看数据范围,\(60\)次询问大概足够二分\(3\)轮了。
那我们来考虑怎么二分。
假设我们已知在区间\([l,r]\)中有被选中的位置,则我们询问\([mid,mid+1]\)。
若返回\(TAK\),说明在区间\([l,mid]\)中必然有被选中的位置,否则说明在区间\([mid+1,r]\)中必然有被选中的位置。
按这样二分,只需一轮下来我们就能求出一个位置了。
然后考虑怎么求第二个位置,显然不能再按上面的套路了,否则只会得到一个完全一样的结果。
设第一个答案为\(t1\),如果我们在\([1,t1-1]\)中以同样的方式进行一轮二分,不就可以得出一个异于\(t1\)的新答案了吗?
然而,如果\([1,t1-1]\)中并没有答案呢?
别着急,这就是题目给我们二分\(3\)轮的次数的原因。如果\([1,t1-1]\)中没有答案,我们就到\([t1+1,n]\)中再进行第三轮二分,这样就肯定能求出另一个答案了。
整理下上面的思路,最后再讲一个细节问题:如何判断\([1,t1-1]\)中没有答案。
对于在\([1,t1-1]\)中二分出的结果\(t2\),如果\(t2=t1\),或者询问\(t2,t1\)得到\(NIE\)(因为对于\(t1\)来说最近距离为\(0\),得到\(NIE\)说明\(t2\)最近距离大于\(0\),即其本身并非答案),就说明\(t2\)是个伪答案。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define write(op,x,y) (printf("%d %d %d\n",op,x,y),fflush(stdout))//交互输出
#define Check() (scanf("%s",s+1),s[1]=='T')//读入结果
using namespace std;
int n,k;char s[10];
I int Solve(RI l,RI r)//二分
{
RI mid;W(l<r) mid=l+r>>1,write(1,mid,mid+1),
scanf("%s",s+1),s[1]=='T'?r=mid:l=mid+1;return l;
}
int main()
{
scanf("%d%d",&n,&k);RI t1,t2;t1=Solve(1,n),//求出第一个答案
((t2=Solve(1,t1-1))==t1||!(write(1,t2,t1),Check()))&&(t2=Solve(t1+1,n));//求出第二个答案
return write(2,t1,t2),0;
}