CF1406E - Deleting Numbers(数学,构造)

题目

交互题,你有一个\(\{1,...,n\}\)的集合,你想要找出其中一个数\(x\),有3个操作:

  • A a:询问当前集合中有多少元素是\(a\)的倍数。
  • B a:询问当前集合中有多少元素是\(a\)的倍数,然后从集合中将\(a\)的倍数的数全部删去,除了\(x\)\(x\)永远不会被删除,即使它是\(a\)的倍数。注意这个操作要求\(a>1\)
  • C a:代表你找到了\(x\)\(x=a\)。结束。

你最多可以执行操作\(10000\)次,\(n\le 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 '\n'
#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;
}
posted @ 2021-09-12 15:48  limil  阅读(27)  评论(0编辑  收藏  举报