题目
交互题,你有一个({1,...,n})的集合,你想要找出其中一个数(x),有3个操作:
- A a:询问当前集合中有多少元素是(a)的倍数。
- B a:询问当前集合中有多少元素是(a)的倍数,然后从集合中将(a)的倍数的数全部删去,除了(x)。(x)永远不会被删除,即使它是(a)的倍数。注意这个操作要求(a>1)。
- C a:代表你找到了(x),(x=a)。结束。
你最多可以执行操作(10000)次,(nle 100000)。
题解
很自然就想到要枚举质数去询问。(10^5)以内质数个数为(9592)。假设可以通过询问一轮知道(x)包含的质因子为(p_1,p_2,...,p_k),那么就可以枚举质因子的幂次进行询问,得到真正的(x),这一步最多只需要不到20次操作。
如何得到(x)包含的质因子?从小到大枚举质数进行B询问,假设当前枚举到(p_{i+1}),已经知道(x)包含(p_1,p_2,...,p_i),那么(x)包含(p_{i+1})的条件就是操作“B (p_1p_2...p_{i+1})”返回的结果为1。这一步最多需要(9592)次操作。那么关键就是如何知道(x)包含的第一个(p_1)。
用分块的思想,每(T)次B操作后执行一次“A 1”,判断集合中剩余个数是否多1,如果是说明这段区间内的存在质数为(x)的质因子。然后遍历这段的质数挨个检查即可。当获得大于等于1个质数后,就可以用上面的方法找出剩余的质因子,故最多就只需遍历一个(T)次。这步最多需要(frac{9592}{T}+T)次操作,取(T=sqrt{9592}=98),即最多(196)次操作。
故总的最多操作为(196+9592+20+1=9809<10000)。
#include <bits/stdc++.h>
// #define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e5 + 1;
const double eps = 1e-5;
bool vis[N], isnp[N];
int pri[N], cnt;
vector<int> d;
int ret = 0;
int del(int p, int n) {
int res = 0;
for(int i = p; i <= n; i += p) {
if(!vis[i]) {
res++;
vis[i] = 1;
}
}
return res;
}
int n;
int main() {
// IOS;
for(int i = 2; i < N; i++) {
if(!isnp[i]) {
pri[cnt++] = i;
}
for(int j = 0; j < cnt && i * pri[j] < N; j++) {
isnp[i * pri[j]] = 1;
if(i % pri[j] == 0) {
break;
}
}
}
cin >> n;
int m = 0;
for(int i = 0; i < cnt; i++) {
m++;
if(pri[i] > n) {
break;
}
}
int sq = sqrt(m), pre = 0;
ll k = 1;
int ret = n;
for(int i = 0; i < m && k * pri[i] <= n; i++) {
int tnum, num;
cout << "B " << k * pri[i] << endl;
cin >> tnum;
num = del(k * pri[i], n);
ret -= num;
if(!d.empty()) {
if(num != tnum) {
d.push_back(pri[i]);
k *= pri[i];
}
} else if((i - pre + 1) % sq == 0 || i == m - 1) {
int r;
cout << "A 1" << endl;
cin >> r;
if(ret != r) {
for(int j = pre; j <= i; j++) {
int flag;
cout << "A " << pri[j] << endl;
cin >> flag;
if(flag) {
d.push_back(pri[j]);
k *= pri[j];
}
}
}
pre = i + 1;
}
}
for(int i = 0; i < d.size() && k <= n; i++) {
ll pw = d[i];
while(k * pw <= n) {
int flag;
cout << "A " << k * pw << endl;
cin >> flag;
if(!flag) {
k = k * (pw / d[i]);
break;
}
pw *= d[i];
if(k * pw > n) {
k = k * (pw / d[i]);
break;
}
}
}
cout << "C " << k << endl;
}