题意
给出正整数n,你要把1-n之间的正整数分成尽可能多组,使得每一组两个数的最大公约数大于1;输出能分成最多组的个数,并按任意顺序输出每组的两个数.
考虑约数最大化利用(或者是尽量用上较大的素数)
发现n/2以上的素数没有用 考虑n/2以下的素数
从大往小尽量找自己的倍数
不明白可以想一下 拿一个大偶数去匹配2 和拿4匹配2 哪一个更划算
然后如果有奇数个倍数就拿出去匹配最小的质数
也就是把2倍拿出去与其他偶数匹配
复杂度 遍历所有因子应该是O(nlgn) (反正O(nsqrt(n))也能过)
Time cost:75min
Code:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #define ms(a,b) memset(a,b,sizeof a) #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define inf 2147483647 using namespace std; typedef long long ll; ll read() { ll as = 0,fu = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') fu = -1; c = getchar(); } while(c >= '0' && c <= '9') { as = as * 10 + c - '0'; c = getchar(); } return as * fu; } const int N = 1000005; //head int n; vector<int> v; int mindiv[N],prim[N],top; void initprim(int n) { rep(i,2,n) { if(!mindiv[i]) prim[++top] = i; for(int j = 1;j <= top && prim[j] * i <= n;j++) { mindiv[i * prim[j]] = prim[j]; if(i % prim[j] == 0) break; } } } bool vis[N]; pair<int,int> tmp[N]; int ans; int main() { scanf("%d",&n); initprim(n>>1); per(i,top,1) { v.clear(); for(int j = prim[i];j <= n;j += prim[i]) { if(!vis[j]) v.push_back(j); vis[j] = 1; } int sze = v.size(); if(sze & 1) sze--,swap(v[1],v[sze]),vis[v[sze]] = 0; for(int j = 0;j < sze;j += 2) { tmp[++ans] = make_pair(v[j],v[j+1]); } } printf("%d ",ans); rep(i,1,ans) printf("%d %d ",tmp[i].first,tmp[i].second); return 0; }