Link.
Description.
交互,初始有一个集合 \(S=\{1,2,\cdots,n\}\),你需要询问一个数 \(x\)。
你可以进行三种询问:
- 问有多少个数是 \(a\) 的倍数
- 问有多少个数是 \(a\) 的倍数,并把 \(a\) 倍数删除,\(x\) 无法被删除
- 回答你找到了 \(x\)。
\(n\le 10^5,\text{limit}\le 10000\)
Solution.
首先肯定大质数、小质数分别处理。
小质数肯定可以随便处理,因为总个数不超过 \(\mathcal O(\sqrt n)\)。
就直接先消,然后枚举指数。
主要是大质数怎么处理。
如果有小质数,那每个大质数 \(a\) 的倍数除了 \(a\) 和 \(x\) 其他都被消了。
所以可以 \(1\) 次判断是否存在 \(a|x\)
如果没有小质数,相当于答案是个质数,此时我们无法按照之前的方法判断。
有个朴素的想法是删一次问一次,这样是 \(O(2n)\) 的,无法通过。
考虑分块,对整块问一次,如果有再继续问,这样就变成 \(O(n+2\sqrt n)\) 了。
Coding.
点击查看代码
//Coded by Kamiyama_Shiki on 2021.11.15 {{{
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=100005;int n;
const int V=N;int pr[V],pc,ls[V];char pv[V];//prinit{{{
inline void prinit(int n=V-1)
{
pv[1]=1,ls[1]=0;for(int i=1;i<=n;i++)
{
if(!pv[i]) pr[++pc]=i,ls[i]=i;
for(int j=1;j<=pc&&i*pr[j]<=n;j++) {pv[i*pr[j]]=1;if(i%pr[j]==0) break;}
}
}//}}}
inline int A(int x)
{
if(x>n) return 0;
printf("A %d\n",x),fflush(stdout);
int w;return read(w),w;
}
inline int B(int x)
{
if(x>n) return 0;
printf("B %d\n",x),fflush(stdout);
int w;return read(w),w;
}
inline void C(int x) {printf("C %d\n",x),fflush(stdout);}
int main()
{
int rs=1,wh=1;read(n),prinit(n);
for(int i=1;i<=pc&&pr[i]*pr[i]<=n;i++)
{
int x=pr[i],nw=0;B(pr[i]),nw=A(pr[i]);
wh=i+1;while(nw) rs*=pr[i],nw=A(x*=pr[i]);
}
if(rs!=1)
{
for(int i=wh;i<=pc;i++) if(A(pr[i])>1) return C(pr[i]*rs),0;
return C(rs),0;
}
for(int l=wh,r=l+99,now=A(1),nx;l<=pc;l+=100,r+=100,now=nx)
{
for(int i=l;i<=r&&i<=pc;i++) B(pr[i]);
nx=A(1);if(now==nx+min(r,pc)-l+1) continue;
for(int i=l;i<=r&&i<=pc;i++) if(A(pr[i])) return C(pr[i]),0;
}return C(1),0;
}