把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1406E】Deleting Numbers(交互)

点此看题面

大致题意: 给定一个数\(n(n\le10^5)\),表示有一个集合其中元素为\(1\sim 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=1\sim m\),每次不断尝试给\(ans\)乘上\(d_i\),即如果\(x\)\(ans\times d_i\)的倍数就更新\(ans\)

由于更新\(ans\)最多只有\(\log n\)次,操作次数可以保证。

但此时我们只考虑了答案中小质数的部分,它还可能含有一个大质数。

这里先讲一个性质:在我们删完所有小质数的倍数之后,剩下的数恰好是\(1\)和所有的大质数(可能还有答案\(x\))。(显然)

因此,由于答案最多只含有一个大质数,我们完全可以去枚举所有大质数\(p\),如果\(A(p)=2\)(即\(p\)\(p\times ans\)),那么答案就是\(p\times 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\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;
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;//注意最后不完整的一段
}
posted @ 2020-09-17 21:18  TheLostWeak  阅读(185)  评论(0编辑  收藏  举报