大致题意: 给定一个数(n(nle10^5)),表示有一个集合其中元素为(1sim n)。现你要通过两种操作在(10000)步以内求出未知数(x):A a
询问当前集合中(a)的倍数个数;B a
询问当前集合中(a)的倍数个数,然后删去所有(a)的倍数(如果(x)是(a)的倍数,则(x)将不会被删去)。
一个初步想法
考虑我们去枚举每一个质数(p),那么只要先(B(p))删去(p)的倍数,再(A(p))询问是否还有(p)的倍数,两次操作就可以判断出(p)是否为(x)的因数了。
然而,(10^5)以内的质数有九千多个,光这么搞一波下来就直接挂了。
套路分类讨论
套路地去分类讨论,把质因数分为小于等于(sqrt n)的(称为小质数)和大于(sqrt n)的(称为大质数)两类。
然后我们就可以把(x)分成两类:含有小质数、只含大质数。
第一类:含有小质数
小质数的数量不多,我们可以按照之前的初步想法,暴力判断出每个小质数是否为(x)的因数。
假设小质数中(x)的因数序列为(d_1,d_2,...,d_m)。
显然(ans)至少为(prod_{i=1}^md_i)。
然后我们枚举(i=1sim m),每次不断尝试给(ans)乘上(d_i),即如果(x)是(ans imes d_i)的倍数就更新(ans)。
由于更新(ans)最多只有(log n)次,操作次数可以保证。
但此时我们只考虑了答案中小质数的部分,它还可能含有一个大质数。
这里先讲一个性质:在我们删完所有小质数的倍数之后,剩下的数恰好是(1)和所有的大质数(可能还有答案(x))。(显然)
因此,由于答案最多只含有一个大质数,我们完全可以去枚举所有大质数(p),如果(A(p)=2)(即(p)和(p imes ans)),那么答案就是(p imes ans)。
第二类:只含大质数
这一部分的核心就是之前已经介绍过的重要性质:在我们删完所有小质数的倍数之后,剩下的数恰好是(1)和所有的大质数。
再重新回头考虑一下我们的初步想法,发现它的实质就是操作每一个质数,然后每次操作完立刻检验。
那么就有一种奇妙的思路:我们可不可以不要每次操作完立刻检验,而是每隔(S)个数检验一次?
显然是可以的。
我们只要记一下删去(S)个数之后本应剩余的数的个数(k),如果实际个数(A(1)=k+1),说明答案就在这(S)个数之中。
然后在这(S)个数中再扫一遍各做一次(A)操作就可以找到答案了。
至于(S)的取值,就是要最小化(S+frac {G}S),肯定是取(sqrt{G})啦。
代码
#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 N 100000
#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;
int n,s,res,Pt,P[N+5],V[N+5];
I void Sieve()//线性筛预处理质数表
{
for(RI i=2,j;i<=n;++i) for(!P[i]&&(P[++Pt]=i),
j=1;j<=Pt&&i*P[j]<=n;++j) if(P[i*P[j]]=1,!(i%P[j])) break;
}
I int Work(CI l,CI r) {for(RI i=l;i<=r;++i) if(A(P[i])) return P[i];}//在[l,r]中扫一遍求答案
int main()
{
RI i,t=0;scanf("%d",&n),s=sqrt(n),Sieve();
for(i=1;i<=Pt&&P[i]<=s;++i) B(P[i]),A(P[i])&&(V[++t]=P[i]);//求出小质数因子
RI cur=i;if(t)//如果含有小质数
{
RI ans=1;for(i=1;i<=t;++i) ans*=V[i];//先求出ans的下界
for(i=1;i<=t;++i) W(ans*V[i]<=n&&A(ans*V[i])) ans*=V[i];//不断尝试更新ans
for(i=cur;i<=Pt;++i) if(A(P[i])==2) return C(ans*P[i]),0;//找是否还有大质数因子
return C(ans),0;//找不到,直接输出
}
RI k=Pt-i+2;for(s=sqrt(k);i<=Pt;++i)//如果只含大质数
if(B(P[i]),--k,++t==s) {if(A(1)^k) return C(Work(i-s+1,i)),0;t=0;}//隔S个数检验一次
return C(A(1)^1?Work(Pt-t+1,Pt):1),0;//注意最后不完整的一段
}