2020牛客暑期多校训练营(第四场)H、Harder Gcd Problem(思维、数论)

题目链接

题面:
在这里插入图片描述

题意:
1... n 1...n 1...n共n个数字。
从中选出两个子集A、B,其中 A ∩ B = 空。集合A和集合B的大小均为m。
我们对于A集合和B集合中的元素按照某一顺序放置后,可以使得任意的 g c d ( a i , b i ) > 1 gcd(ai,bi)>1 gcd(ai,bi)>1

简化:
把 1~N 的数选尽量多的组,使得每组 gcd 大于 1。
输出任意一种方案

题解:
考虑m最大能取到多大,除了大于 n/2 的质数和 1,其余的数是可以均分到两个集合中的(如果剩余的数是奇数,扔掉一个就好啦)。

考虑如何构造一个可行解。
现在我们已经将大于 n/2 的质数和 1从序列中去除了。
我们按照每个数的最大质因子分类,最大质因子相同的数放在同一类中,那么某一类一定是
p , 2 p , 3 p . . . p,2p,3p... p,2p,3p...等等,可以证明最终一定可以划分为若个类。
这里的p的系数不一定连续,例如 p=3时,那么是 p , 2 p , 3 p , 4 p , 6 p , 8 p p,2p,3p,4p,6p,8p p,2p,3p,4p,6p,8p

如果某个类中有偶数个数,那么他们两两匹配即可。
如果某个类中有奇数个数,那么就将 2p 留出来,放置最大质因子为2的集合中,剩下的两两匹配。
最终最大质因子为2的集合中的数一定都能被2整除。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxm=100100;
const int up=100100;

int prime[maxn],maxx[maxn],cnt;
bool ha[maxn];
vector<int>vc[maxn];
vector<pair<int,int> >ans;

void Prime(void)
{
    ha[1]=true;
    maxx[1]=0;
    for(int i=2;i<maxn;i++)
    {
        if(!ha[i])
        {
            prime[++cnt]=i;
            maxx[i]=cnt;
        }
        for(int j=1;j<=cnt&&prime[j]*i<maxn;j++)
        {
            ha[i*prime[j]]=true;
            maxx[i*prime[j]]=max(maxx[i],j);
            if(i%prime[j]==0) break;
        }
    }
}

int main(void)
{
    int t;
    scanf("%d",&t);
    Prime();
    while(t--)
    {
        int n;
        scanf("%d",&n);
        vc[1].clear();
        int ma=0;
        for(int i=2;i<=n;i++)
        {
            vc[i].clear();
            if(i>n/2&&ha[i]==false) continue;
            vc[maxx[i]].pb(i);
            ma=max(ma,maxx[i]);
        }
        ans.clear();
        for(int i=ma;i>=2;i--)
        {
            if(vc[i].size()==0) continue;
            if(vc[i].size()%2==0)
            {
                for(int j=0;j<vc[i].size();j+=2)
                    ans.pb(pr(vc[i][j],vc[i][j+1]));
            }
            else
            {
                ans.pb(pr(vc[i][0],vc[i][2]));
                vc[1].pb(vc[i][1]);
                for(int j=3;j<vc[i].size();j+=2)
                    ans.pb(pr(vc[i][j],vc[i][j+1]));
            }
        }
        //2
        for(int i=0;i+1<vc[1].size();i+=2)
            ans.pb(pr(vc[1][i],vc[1][i+1]));

        printf("%d\n",ans.size());
        for(int i=0;i<ans.size();i++)
            printf("%d %d\n",ans[i].first,ans[i].second);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值