CF1406E Deleting Numbers

本来是本着口胡一下的原则去想的,但是最后又调了一个晚上

首先我们有一个很naive的想法,可以枚举每一个质数\(p\),先删去它的倍数然后在问是否有\(p\)的倍数,就可以用两次操作判断出\(p\)是否为\(x\)的倍数了

然后我们粗略一算,\(10^5\)以内的质数有九千多个,直接GG了

但是我们发现这个想法很好,因为涉及到质数的情况很容易按\(\sqrt n\)分开来讨论

因此我们考虑先把\(\sqrt n\)内的\(x\)的约数序列\(d_{1\cdots 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\times cur\)),答案就是\(p\times cur\)

所以接下来我们就要讨论答案就是一个大质数的情况了,由于大质数很多,如果我们还是对于每个数都花上\(2\)次操作去询问那么显然还是GG了

于是陈指导提出了奇妙的阈值做法,具体地,我们设\(S\)表示删去连续的\(S\)个数之后来检验一次答案是否在这\(S\)个数之中

若在的话我们只需要在这\(S\)个数之件扫一遍进行询问就可以得出答案了

所以我们要最小化\(S+\frac{num}{S}\)\(num\)表示剩下的大质数数量),取\(S=\sqrt {num}\)即可,此时询问次数\(num+2\sqrt {num}\)

实测可以完美的卡着上限通过此题

#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
#define A(x) (printf("A %d\n",x),fflush(stdout),scanf("%d",&res),res)
#define B(x) (printf("B %d\n",x),fflush(stdout),scanf("%d",&res),res)
#define C(x) (printf("C %d\n",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;
}
posted @ 2020-09-17 21:36  空気力学の詩  阅读(188)  评论(0编辑  收藏  举报