CF449C Jzzhu and Apples (筛素数 数论

题意:有N个苹果,编号为1~N。现要将苹果组成若干对,每对苹果最小公约数不为1。求最多能分成多少对,输出各对的组成。

题解:先筛素数,然后搞。

首先我们怕的是乱选了两个数组成了公约数不为1的一对,但是这导致了总对数减少(这两个数分别被别的数需要,它们组成一对了不是最优解)。为了防止这种情况,我们要想办法让总对数不会减少。

我们发现2的倍数们非常碉炸,任意2个就能组成1对,所以我们先弄其他的数,最后再搞2的倍数。

我们发现一个质数x的1倍、2倍、3倍、……?倍中未使用的数组成的集合,也可以任意两两组合,但是如果在1~n之间,这个集合的元素个数是奇数,就会多一个。为了不造成多余的影响,我们把2*x作为多出来的一个,扔到2的倍数中去。这样,各种集合的多出来的一个,肯定能找到配对。把我们使用的数标记一下,防止搞其他质数的时候重复用一个数。

先搞完3到小于等于(N/2)的质数(大于N/2,它的倍数的集合就只有它自己了,没法玩),然后搞2的倍数,把之前扔进来的2*x们和其他2*y(y是合数)组成一个大集合,两两配对,最后再多出来一个也没办法了,这已经是最多的配对了。

解不唯一,我们这样搞肯定能找到最多的对数


//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
#define usll unsigned ll
#define mz(array) memset(array, 0, sizeof(array))
#define minf(array) memset(array, 0x3f, sizeof(array))
#define REP(i,n) for(i=0;i<(n);i++)
#define FOR(i,x,n) for(i=(x);i<=(n);i++)
#define RD(x) scanf("%d",&x)
#define RD2(x,y) scanf("%d%d",&x,&y)
#define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define WN(x) prllf("%d\n",x);
#define RE  freopen("D.in","r",stdin)
#define WE  freopen("1biao.out","w",stdout)
#define mp make_pair
#define pb push_back

const long N = 100001;
int prime[N],pn = 0;
bool isnp[N];
void shai() {
    int i,j;
    memset(prime,0,sizeof(prime));
    memset(isnp,0,sizeof(isnp));
    isnp[0]=1,isnp[1]=1;
    pn=0;
    for(i = 2 ; i < N ; i ++) {
        if(! isnp[i])
            prime[pn ++]=i;
        //关键处1
        for(j = 0 ; j < pn && i * prime[j] <  N ; j ++) {
            isnp[i * prime[j]] = 1;
            if( !(i % prime[j] ) )  //关键处2
                break;
        }
    }
}

int n;
vector<int>a,a2;
vector<pair<int,int> >v;
bool used[N];
int main() {
    int i,j,k;
    int l,r,mid;
    int pre;
    int ans;
    shai();
    while(scanf("%d",&n)!=EOF) {
        ans=0;
        v.clear();
        a2.clear();
        memset(used,0,sizeof(used));

        for(i=1; prime[i]<=n/2; i++) {
            a.clear();
            a.pb(prime[i]);
            for(j=3*prime[i]; j<=n; j+=prime[i]) if(!used[j]) a.pb(j);
            if(a.size()%2==0) a2.pb(2*prime[i]);
            else a.pb(2*prime[i]);
            int maxj=a.size();
            for(j=0; j+1<maxj; j+=2) {
                v.pb(mp(a[j],a[j+1]));
                used[a[j]]=1;
                used[a[j+1]]=1;
            }
        }

        if(n>=2)a2.pb(2);
        if(n>=4)a2.pb(4);
        for(i=4; i+i<=n; i++) {
            if(!used[i+i] && isnp[i]) a2.pb(i+i);
        }
        int maxi=a2.size();
        for(i=0; i+1<maxi; i+=2) v.pb(mp(a2[i],a2[i+1]));
        printf("%d\n",v.size());
        maxi=v.size();
        REP(i,maxi) printf("%d %d\n",v[i].first,v[i].second);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值