题解 UVA1521 【GCD Guessing Game】
STDquantum · · 题解
这是一篇解释“为什么想到要选质数”以及“为什么选质数是对的”(也就是其他大佬面临的问题)的题解。
如果出错欢迎提出。
UPD on 2020.10.15:更改了证明中错误的部分。感谢SuperTNT、陈浩然纠错,感谢minxu帮助完善证明。
正解不是我想出来的,感谢shame_djj和naive_wcx大佬。
做这道题的时候思路可以是这样的,先考虑非最优的策略,再去想怎么优化它。
所以一开始想的时候,就是先从最大值(也就是
最坏是
我们先对排除做一个定义吧,就是:猜想一个
当然我们可以发现最难找的肯定就是
接下来证明找到
所以一定不存在一个数需要排除的步数比
那么,我们就是要找一个最优的方案让我们非常快速地把数筛掉,那么显然对于
而
接下来就是贪心的事了,用质数来筛是最优的(详情见筛质数的算法,在筛出质数的同时筛了所有其他数),所以我们要把
但其实在本题所给的范围中,这样做与下面做法是等价的:每个小于
这个做法我并不会证明,但是是对的,代码将在文末给出。
另外:还要说明一点,打包时选质数的顺序(从大到小)并不是实际操作的顺序,实际操作时需要小的质因子优先询问,也就是为了解决形如
这样每次是
#include <bits/stdc++.h>
using namespace std;
namespace STDquantum {
const int N = 1e4 + 10;
int num, prime[N];
bool isPrime[N];
inline void Get(int n) { // 线性筛质数
memset(isPrime, 1, sizeof(isPrime));
isPrime[1] = 0;
for (int i = 2; i <= n; ++i) {
if (isPrime[i]) prime[++num] = i;
for (int j = 1; j <= num && i * prime[j] <= n; ++j) {
isPrime[i * prime[j]] = 0;
if (i % prime[j] == 0) break;
}
}
}
int n, ans;
inline void main() {
Get(N - 10);
while (~scanf("%d", &n) && n) {
ans = 0;
int l = 1, r = num;
while (prime[r] > n) --r; // 让prime[r]降到[2,n]的范围
for (int k; l <= r; --r, ++ans) {
k = prime[r];
while (k * prime[l] <= n) k *= prime[l++];
}
printf("%d\n", ans);
}
}
} // namespace STDquantum
int main() {
STDquantum::main();
return 0;
}
悄咪咪说一句,POJ上没有题面的4028和这题一毛一样……
本题中循环可以简化为如下形式:
for (; l <= r; --r, ++ans) {
if (prime[r] * prime[l] <= n) ++l;
}