根分+数论,hot tea

Codeforces 题面传送门 & 洛谷题面传送门

一道个人感觉挺有意思的交互题,本人一开始想了个奇奇怪怪的做法,还以为卡不进去,结果发现竟然过了,而且还是正解(

首先看到这类题目可以考虑每次删掉一个质数的倍数,即对某个 \(pr_i\) 执行 B 操作,然后对 \(pr_i\) 进行 A 询问,根据询问得到的值是否等于理论上删去这些数后 \(pr_i\) 的倍数个数来判断 \(pr_i\) 是否是 \(x\) 的因数,如果是那么就枚举 \(pr_i\) 的 \(2,3,4,\cdots\) 次方,再对这些次方执行 A 询问,以此来判断 \(x\) 质因数分解式中 \(pr_i\) 的次数。

算下询问次数,询问 \(pr_i\) 的 \(2,3,4,\cdots\) 的次数显然与 \(x\) 质因数分解式中 \(x\) 每个质因数的次数之和有关,是 \(\log\) 级别的,最多只有 \(20\) 可以忽略,瓶颈在于每个质因数都要进行两次操作,打表可以发现 \(10^5\) 以内质因数个数刚好比 \(10^4\) 小个几百,因此 \(2\pi(n)\) 是 \(2\times 10^4\) 级别的,过不去。

考虑优化,很明显对于 B 操作而言,我们是可以在操作的同时问出 \(x\) 的倍数的,而刚刚的解法中并没有利用这个性质,因此考虑从这个性质入手解题,我们还是从小到大枚举质因子,只不过与刚才不同的一点是,我们不每进行一次 B 询问都跟一个 A 询问,我们只做 B 询问,那么对于大多数质数,每次 B 询问时也可以根据结果知道该质数是否是 \(x\) 的因子,只有一个例外——那就是 \(x\) 的最小质因子,因此接下来我们只用找到 \(x\) 的最小质因子及它的次数即可,这也是本题的难点所在,我就在这个地方卡了 20+min 后想到了这个解法。

注意到 A 操作起始下标是可以从 \(1\) 开始的,而通过 \(A\ 1\) 我们则可以知道刚才询问的数中是否出现了 \(x\) 的质因子,因此我们考虑根分,每 100 次 B 询问来一次 A 1,那么我们可以把质因子按大小分为 \(\lceil\dfrac{\pi(n)}{100}\rceil\),显然最小质因子就在最后一个 A 1 等于剩余数个数与第一个 A 1 不等于剩余数个数的位置之间,中间部分就暴力枚举质因子然后用 A 质因子判断即可,这样总操作次数为 \(\pi(n)+100+\lceil\dfrac{\pi(n)}{100}\rceil+\log n\),实测询问次数最多 9700+ 次,刚好卡过。

const int MAXN=1e5;
int n,pr[MAXN/5+5],prcnt=0;
bool vis[MAXN+5],is[MAXN+5],has[MAXN+5];
vector<int> fac[MAXN+5];
int cnt[MAXN+5];
void sieve(){
	for(int i=2;i<=n;i++){
		if(!vis[i]) pr[++prcnt]=i;
		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
			vis[pr[j]*i]=1;
			if(i%pr[j]==0) break;
		}
	}
	for(int i=1;i<=n;i++) cnt[i]=n/i;
	for(int i=1;i<=prcnt;i++) for(ll j=pr[i];j<=n;j*=pr[i]) is[j]=1;
	for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) fac[j].pb(i);
}
void del(int x){for(int y:fac[x]) cnt[y]--;has[x]=1;}
void del_mul(int x){for(int i=x;i<=n;i+=x) if(!has[i]) del(i);}
int qr1(int x){printf("A %d\n",x);fflush(stdout);int res;scanf("%d",&res);return res;}
int qr2(int x){printf("B %d\n",x);fflush(stdout);int res;scanf("%d",&res);return res;}
int main(){
	scanf("%d",&n);sieve();
	int cur=1,mn=prcnt,cc=0,fst_blk=0;
	for(int i=1;i<=prcnt;i++){
		int t=qr2(pr[i]);
		if(t!=cnt[pr[i]]){
			if(cur==1) mn=i;del_mul(pr[i]);
			cur*=pr[i];
			for(ll x=1ll*pr[i]*pr[i];x<=n;x*=pr[i]){
				int num=qr1(x);//printf("%d %d\n",x,cnt[x]);
				if(num==cnt[x]) break;cur*=pr[i];
			}
		} else del_mul(pr[i]);
		++cc;
		if(cc==100&&!fst_blk){
			cc=0;int x=qr1(1);
			if(x!=cnt[1]) fst_blk=i;
		}
	} if(!fst_blk){int x=qr1(1);if(x!=cnt[1]) fst_blk=prcnt;}
	for(int j=(fst_blk-1)/100*100+1;j<=min(fst_blk,mn);j++){
		int x=qr1(pr[j]);
		if(x!=cnt[pr[j]]){
			cur*=pr[j];
			for(ll k=1ll*pr[j]*pr[j];k<=n;k*=pr[j]){
				int num=qr1(k);//printf("%d %d\n",x,cnt[x]);
				if(num==cnt[k]) break;cur*=pr[j];
			} break;
		}
	}
	printf("C %d\n",cur);fflush(stdout);
	return 0;
}