zoukankan      html  css  js  c++  java
  • 【CF1406E】Deleting Numbers(交互)

    点此看题面

    大致题意: 给定一个数(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;//注意最后不完整的一段
    }
    
  • 相关阅读:
    code war 天天一练(4)
    code war 天天一练(3)
    code war 天天一练(2)
    code war 天天一练(1)
    手写promise
    JSON序列化和反序列化
    SqlBulkCopy高效插入数据
    C#反射方法示例
    《将博客搬至CSDN》
    PHP学习笔记——Php文件引入
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1406E.html
Copyright © 2011-2022 走看看