大致题意: 交互题。已知在一个长度为(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
",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;
}