zoukankan      html  css  js  c++  java
  • CF1406E Deleting Numbers

    题目来源:Codeforces, CF1406E Deleting Numbers

    题目大意

    题目链接

    交互题。

    有一个未知的整数 (x) ((1leq xleq n)),你需要通过交互找出它。

    初始时,交互库会告诉你 (n)

    有一个集合 ({1,2,dots ,n})。你可以进行不超过 (10^4) 次操作。每次操作可以是如下三种之一:

    • ( ext{A }a):向交互库询问,当前集合里有多少数是 (a) 的倍数。
    • ( ext{B }a):向交互库询问,当前集合里有多少数是 (a) 的倍数。然后将所有 (a) 的倍数删去。特别地:(x) 将不会被删去,即使它是 (a) 的倍数。此操作必须保证 (a > 1)
    • ( ext{C }a):向交互库报告你找出的 (x)

    请在规定操作次数内求出 (x)

    数据范围:(1leq nleq 10^5)

    本题题解

    考虑一种暴力做法。枚举 (1dots n) 中每个质数 (p),再枚举 (p)(n) 以下的所有次幂 (p^k)。先用 ( ext{B}) 操作删除 (p^k) 的倍数,再用 ( ext{A}) 操作查询集合里是否仍有 (p^k) 的倍数。若有,说明 (x)(p^k) 的倍数。这样,我们可以知道每个质数在 (x) 里的最大次幂是多少,根据唯一分解定理,我们也就确定了 (x)

    (pi(n))(1dots n) 中的质数数量,( au(n))(1dots n) 中的质数的幂的数量。打表发现,(pi(10^5)=9592, au(10^5)=9700)。上述做法,需要 (2 au(n)+1) 次操作(最后一次操作是报告答案),无法通过本题。

    一个小优化是,对每个质数 (p),先用 ( ext{B}) 操作删除所有 (p) 的倍数,再依次询问所有次幂。操作次数优化为 (pi(n)+ au(n)+1),但仍无法通过本题。

    有一个经典的技巧是,把质数分为 (leq sqrt{n}) 的“小质数”和 (> sqrt{n}) 的“大质数”。小质数总共只有 (pi(sqrt{n})) 个。而大质数在任何 (x) 里至多只有 (1) 个。

    先用 (pi(sqrt{n})+ au(sqrt{n})leq 238) 次操作,求出 (x) 中所有小质因子及其次幂。设 (x) 的小质因子部分为 (x_{ ext{small}})。此时,集合里还剩下 (1)(x)、以及所有大质数。我们要确定 (x) 里还剩下的那 (1) 个大质因子是谁(或者根本没有大质因子)。

    在前面的朴素做法里,我们实际对每个大质数用了 (2) 次询问,这样太多了。考虑减小这个次数。分两种情况:

    • 如果 (x_{ ext{small}} > 1):我们对每个大质数 (p),直接用 ( ext{A}) 操作询问 (p imes x_{ ext{small}})。如果结果为 (1),说明 (x) 就等于 (p imes x_{ ext{small}})(不然的话,这个数此时应该已经被删了);如果结果为 (0),说明 (p) 不是 (x) 的大质因子。这样,对每个大质因子只需要 (1) 次操作。找出大质因子所需的操作次数等于大质因子的数量,为:(pi(n)-pi(sqrt{n}))。加上小质数的部分,以及报告答案的操作,总次数为 (pi(sqrt{n})+ au(sqrt{n})+(pi(n)-pi(sqrt{n}))+1=pi(n)+ au(sqrt{n})+1leq 9766)
    • 如果 (x_{ ext{small}}=1):此时 (p imes x_{ ext{small}}) 就是大质数 (p) 本身,它本来就应该在集合里,所以直接询问是没有意义的。这种情况下,集合里只剩 (1) 和大质数(因为 (x) 本身也是 (1) 或大质数)。考虑大质数序列分块。每块里,先依次用 ( ext{B}) 操作将本块的大质数全部删除,然后用 ( ext{A}) 操作对 (1) 进行一次询问(也就是询问整个集合剩余的数的数量)。如果发现集合大小减少的量,少于我们删除的量,说明 (x) 就在本块里,我们枚举本块中的数,依次询问。这样,设块的大小为 (S),则找出大质数所需的操作次数为:(pi(n)-pi(sqrt{n})+lceilfrac{pi(n)-pi(sqrt{n})}{B} ceil+B)。取 (B = lfloorsqrt{pi(n)-pi(sqrt{n})} floor = 97) 时,这部分的最大操作次数为 (9723)。加上小质数的部分,以及报告答案的操作,最大总次数为 (9962)。可以通过本题。

    参考代码

    // problem: CF1406E
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    
    template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
    template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
    
    const int MAXN = 1e5;
    const int SQRT = 316;
    const int BLOCK_SIZE = 97;
    
    int ask(int x) {
    	cout << "A " << x << endl;
    	int res; cin >> res;
    	return res;
    }
    int ask_and_del(int x) {
    	cout << "B " << x << endl;
    	int res; cin >> res;
    	return res;
    }
    void report_ans(int x) {
    	cout << "C " << x << endl;
    }
    
    int n, ans;
    int p[MAXN + 5], cnt_p;
    bool v[MAXN + 5];
    void sieve(int lim) {
    	for (int i = 2; i <= lim; ++i) {
    		if (!v[i]) {
    			p[++cnt_p] = i;
    		}
    		for (int j = 1; j <= cnt_p && p[j] * i <= lim; ++j) {
    			v[i * p[j]] = 1;
    			if (i % p[j] == 0) break;
    		}
    	}
    }
    int main() {
    	cin >> n;
    	sieve(n);
    	ans = 1;
    	// first part: 
    	int second_part_start = cnt_p + 1;
    	for (int i = 1; i <= cnt_p; ++i) {
    		if (p[i] > SQRT) {
    			second_part_start = i;
    			break;
    		}
    		int res = ask_and_del(p[i]);
    		if (!res) continue;
    		int lastval = 0, flag = 0;
    		for (ll j = p[i]; j <= n; j *= p[i]) {
    			if (!ask(j)) {
    				ans *= j / p[i];
    				flag = 1;
    				break;
    			}
    			lastval = j;
    		}
    		if (!flag) {
    			ans *= lastval;
    		}
    	} // 238
    	
    	// second part:
    	if (ans > 1) {
    		for (int i = second_part_start; i <= cnt_p; ++i) {
    			if ((ll)p[i] * ans > n)
    				continue;
    			if (ask(p[i] * ans)) {
    				ans *= p[i];
    				break;
    			}
    		} // 9765
    		report_ans(ans);
    	} else {
    		// 分块
    		for (int i = second_part_start; i <= cnt_p; i += BLOCK_SIZE) {
    			int j = min(cnt_p, i + BLOCK_SIZE - 1);
    			for (int k = i; k <= j; ++k) {
    				ask_and_del(p[k]);
    			}
    			int res = ask(1);
    			if (res > 1 + cnt_p - j) {
    				assert(res == 1 + cnt_p - j + 1);
    				for (int k = i; k <= j; ++k) {
    					if (ask(p[k])) {
    						ans = p[k];
    						break;
    					}
    				}
    				break;
    			}
    		} // 9961
    		report_ans(ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    RequireJS 和 Sea.js
    zoom:1
    font-sqirrel
    WEB前端面试题 分别使用2个、3个、5个DIV画出一个大的红十字
    获取表单select域的选择部分的文本
    写一个简单的form表单,当光标离开表单的时候表单的值发送给后台
    Python3基础 e记法示例
    Python3基础 response.read 输出网页的源代码
    Python3基础 访问在线的有道词典
    Python3基础 response.info 服务器返回的header信息
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13963433.html
Copyright © 2011-2022 走看看