CodeForces 449C Jzzhu and Apples
原题链接
给出正整数 n,你要把1∼n 之间的正整数两两分组,使得每一组两个数的最大公约数大于1。输出能分成最多个组,并按任意顺序输出每组的两个数。
题解
官方题解 说得很简单。就是枚举从大到小质因数 x(如果2x≥n 显然就不用管了),找出所有之前没有匹配过得 x 的倍数,如果是偶数个就两两匹配,否则把 2x 除去即可。
那为什么这样是对的呢?
我们来看最后枚举到 x=2 的情况。如果有偶数个,那正好可以两两匹配,显然最优。如果是奇数个,那我们发现所有 可能被匹配的数 (即所有枚举到的数)有奇数个,即我们至少需要扔掉一个。现在我们只扔掉了一个(因为之前除去的2x 都在此时被匹配了),所以这种情况是最优的。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <cstdio> #include <algorithm>
using namespace std;
const int maxn = 100005;
int n, prime[maxn >> 1], p[maxn], cnt;
inline void oula(int n) { cnt = 0; for(int i = 2; i <= n; ++i) { if(!p[i]) prime[++cnt] = i; for(int j = 1; j <= cnt && prime[j] * i <= n; ++j) { p[prime[j] * i] = 1; if(!(i % prime[j])) break; } } }
int aa[maxn], cntt, hv[maxn]; int ans[maxn][2], anss;
int main() { scanf("%d", &n); oula(n >> 1); for(int i = cnt; i; --i) { cntt = 0; for(int j = prime[i]; j <= n; j += prime[i]) if(!hv[j]) aa[++cntt] = j; if(cntt & 1) { swap(aa[cntt], aa[2]); cntt--; } for(int j = 1; j <= cntt; j += 2) { hv[aa[j]] = hv[aa[j + 1]] = 1; ans[++anss][0] = aa[j]; ans[anss][1] = aa[j + 1]; } } printf("%d\n", anss); for(int i = 1; i <= anss; ++i) printf("%d %d\n", ans[i][0], ans[i][1]); return 0; }
|