本来是本着口胡一下的原则去想的,但是最后又调了一个晚上
首先我们有一个很naive的想法,可以枚举每一个质数(p),先删去它的倍数然后在问是否有(p)的倍数,就可以用两次操作判断出(p)是否为(x)的倍数了
然后我们粗略一算,(10^5)以内的质数有九千多个,直接GG了
但是我们发现这个想法很好,因为涉及到质数的情况很容易按(sqrt n)分开来讨论
因此我们考虑先把(sqrt n)内的(x)的约数序列(d_{1cdots m})求出来,然后从(cur=prod_{i=1}^m d_i)开始扩展答案
每次尝试不断地给(cur)乘上(d_i),然后判断是否仍为(x)的约数即可
由于每个(d_i)更新(cur)最多只有$log $级别的次数,因此可以保证操作次数
但是我们发现此时(x)可能还有一个约数是大于(sqrt n)的质数,容易发现当我们删除完所有的小质数的倍数后,剩下的数恰好是所有的大质数(或者包含(x))
因此我们可以去枚举所有大质数(p),判断(A(p))是否等于(2)即可(即剩下了(p)和(p imes cur)),答案就是(p imes cur)
所以接下来我们就要讨论答案就是一个大质数的情况了,由于大质数很多,如果我们还是对于每个数都花上(2)次操作去询问那么显然还是GG了
于是陈指导提出了奇妙的阈值做法,具体地,我们设(S)表示删去连续的(S)个数之后来检验一次答案是否在这(S)个数之中
若在的话我们只需要在这(S)个数之件扫一遍进行询问就可以得出答案了
所以我们要最小化(S+frac{num}{S})((num)表示剩下的大质数数量),取(S=sqrt {num})即可,此时询问次数(num+2sqrt {num})
实测可以完美的卡着上限通过此题
#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
#define A(x) (printf("A %d
",x),fflush(stdout),scanf("%d",&res),res)
#define B(x) (printf("B %d
",x),fflush(stdout),scanf("%d",&res),res)
#define C(x) (printf("C %d
",x),fflush(stdout))
using namespace std;
const int N=100005;
int n,p[N],t[N],tot,res,cnt,cur,ct,S; bool vis[N];
inline void init(CI n)
{
RI i,j; for (i=2;i<=n;++i)
{
if (!vis[i]) p[++cnt]=i;
for (j=1;j<=cnt&&i*p[j]<=n;++j)
{
vis[i*p[j]]=1; if (i%p[j]==0) break;
}
}
}
inline int find(CI l,CI r)
{
for (RI i=l;i<=r;++i) if (A(p[i])) return p[i];
}
int main()
{
RI i; for (scanf("%d",&n),init(n),i=1;i<=cnt&&p[i]*p[i]<=n;++i)
if (B(p[i]),A(p[i])) t[++tot]=p[i]; int tp=i; if (tot)
{
for (cur=i=1;i<=tot;++i) cur*=t[i];
for (i=1;i<=tot;++i) while (cur*t[i]<=n&&A(cur*t[i])) cur*=t[i];
for (i=tp;i<=cnt;++i) if (A(p[i])==2) return C(cur*p[i]),0;
return C(cur),0;
}
for (cur=cnt-i+2,S=sqrt(cur);i<=cnt;++i)
if (B(p[i]),--cur,++ct==S) { if (A(1)!=cur) return C(find(i-S+1,i)),0; ct=0; }
return C(A(1)!=1?find(cnt-ct+1,cnt):1),0;
}