数论 + 贪心 - Harder Gcd Problem - 2020牛客暑期多校训练营(第四场)+ Jzzhu and Apples - CF 449C

数论 + 贪心 - Harder Gcd Problem - 2020牛客暑期多校训练营(第四场)+ Jzzhu and Apples - CF 449C

题意:

T 组 测 试 数 据 , T组测试数据, T

每 组 包 括 一 个 整 数 n , 每组包括一个整数n, n

要 求 从 1 到 n 的 排 列 : 要求从1到n的排列: 1n{1,2,…,n}   中 , 选 择 出 两 个 子 集 A 和 B 。 \ 中,选择出两个子集A和B。  AB

满 足 : ∣ A ∣ = ∣ B ∣ = m 且 A ∩ B = ϕ 。 满足:|A|=|B|=m且A∩B=\phi。 A=B=mAB=ϕ

    且 对 于 任 意 的 元 素 a i ∈ A , b i ∈ B , g c d ( a i , b i ) > 1 , 1 ≤ i ≤ m 。 \qquad\ \ \ 且对于任意的元素a_i∈A,b_i∈B,gcd(a_i,b_i)>1,1≤i≤m。    aiAbiBgcd(ai,bi)>11im

求 出 m 的 最 大 值 , 并 输 出 长 度 为 m 的 两 个 集 合 A 和 B 。 求出m的最大值,并输出长度为m的两个集合A和B。 mmAB

输入描述:

首 行 一 个 正 整 数 T , 表 示 测 试 数 据 的 数 量 首行一个正整数T,表示测试数据的数量 T

接 下 来 T 行 , 每 行 输 入 正 整 数 n ( 4 ≤ n ≤ 2 × 1 0 5 ) 。 接下来T行,每行输入正整数n(4≤n≤2×10 ^5)。 Tn(4n2×105)

输出描述:

首 行 一 个 正 整 数 s u m , 表 示 m 的 最 大 值 。 首行一个正整数sum,表示m的最大值。 summ

接 着 输 出 s u m 行 数 , 第 i 行 为 a i 和 b i , 1 ≤ i ≤ m 。 接着输出sum行数,第i行为a_i和b_i,1≤i≤m。 sumiaibi1im

示例1
输入

2
4
10

输出

1
2 4
4
3 9
5 10
8 2
4 6

分析:

出 C F 原 题 是 我 没 想 到 的 ( 出 题 人 是 不 是 有 点 偷 懒 . . . 出CF原题是我没想到的(出题人是不是有点偷懒... CF(...

原题链接:《Jzzhu and Apples》

本 题 只 是 改 成 了 T 组 测 试 数 据 。 本题只是改成了T组测试数据。 T

分 析 一 下 贪 心 策 略 吧 , 下 午 做 的 时 候 是 想 得 很 接 近 了 , 写 到 一 半 放 弃 ( 蔡 。 分析一下贪心策略吧,下午做的时候是想得很接近了,写到一半放弃(蔡。 (

首 先 应 该 能 够 发 现 , 有 公 约 数 的 数 之 间 可 以 任 意 匹 配 , 特 殊 的 是 质 数 , 首先应该能够发现,有公约数的数之间可以任意匹配,特殊的是质数,

对 于 质 数 P , 若 P × 2 > n , 则 无 需 考 虑 这 个 质 数 。 对于质数P,若P×2>n,则无需考虑这个质数。 PP×2>n

对 于 满 足 2 × P ≤ n 的 质 数 , 可 以 用 P 的 倍 数 与 之 配 对 , 这 样 才 能 保 证 尽 量 多 的 数 被 使 用 了 。 对于满足2×P≤n的质数,可以用P的倍数与之配对,这样才能保证尽量多的数被使用了。 2×PnP使

以 因 子 P 为 划 分 依 据 , 将 P 的 倍 数 都 存 储 到 同 一 个 集 合 中 去 , 相 同 集 合 中 任 意 两 个 数 均 能 形 成 一 对 匹 配 。 以因子P为划分依据,将P的倍数都存储到同一个集合中去,相同集合中任意两个数均能形成一对匹配。 PP

输 出 时 , 我 们 将 同 一 集 合 中 的 元 素 两 两 输 出 即 可 。 输出时,我们将同一集合中的元素两两输出即可。

注意:

① 、 要 从 大 到 小 枚 举 质 数 P 。 ①、要从大到小枚举质数P。 P

理 由 : 假 设 有 P 1 < P 2 , P 1 所 在 集 合 中 S P 1 , P 2 所 在 集 合 S P 2 。 \qquad理由:假设有P_1<P_2,P_1所在集合中S_{P_1},P_2所在集合S_{P_2}。 P1<P2P1SP1P2SP2

其 中 S P 1 = \qquad其中S_{P_1}= SP1={ P 1 , 2 P 1 , . . . , ⌊ n P 1 ⌋ P 1 P_1,2P_1,...,\lfloor\frac{n}{P_1}\rfloor P_1 P1,2P1,...,P1nP1}, S P 2 = S_{P_2}= SP2={ P 2 , 2 P 2 , . . . , ⌊ n P 2 ⌋ P 2 P_2,2P_2,...,\lfloor\frac{n}{P_2}\rfloor P_2 P2,2P2,...,P2nP2},

则 必 有 ∣ S P 1 ∣ > ∣ S P 2 ∣ , 若 存 在 S P 1 ∩ S P 2 = k P 1 , 此 时 我 们 让 k P 1 与 S P 2 中 的 元 素 匹 配 才 是 最 优 的 。 \qquad则必有|S_{P_1}|>|S_{P_2}|,若存在S_{P_1}∩S_{P_2}=kP_1,此时我们让kP_1与S_{P_2}中的元素匹配才是最优的。 SP1>SP2SP1SP2=kP1kP1SP2

② 、 当 集 合 中 的 元 素 大 于 1 且 为 奇 数 时 , 我 们 就 将 2 × P 保 留 下 来 , 将 剩 下 的 数 两 两 匹 配 。 ②、当集合中的元素大于1且为奇数时,我们就将2×P保留下来,将剩下的数两两匹配。 12×P

因 为 , 2 是 最 小 的 质 数 , 可 操 作 性 更 强 , 即 集 合 S 2 中 的 元 素 数 量 最 多 。 \qquad因为,2是最小的质数,可操作性更强,即集合S_2中的元素数量最多。 2S2

③ 、 最 后 要 将 所 有 未 被 匹 配 的 偶 数 累 加 上 , 即 因 子 为 2 ( 最 小 的 质 数 ) 的 未 被 匹 配 的 数 的 集 合 。 ③、最后要将所有未被匹配的偶数累加上,即因子为2(最小的质数)的未被匹配的数的集合。 2()

疑问:这样的做法能够保证考虑过1~n中的所有数了吗?

答 案 是 肯 定 的 , 参 考 埃 式 筛 法 。 答案是肯定的,参考埃式筛法。

因 为 每 个 质 数 都 被 考 虑 到 了 , 且 每 个 质 数 的 倍 数 也 都 被 对 应 考 虑 到 了 。 因为每个质数都被考虑到了,且每个质数的倍数也都被对应考虑到了。

另外:

输 出 时 下 标 可 从 1 开 始 两 两 输 出 , 可 以 直 接 过 滤 掉 数 组 中 的 元 素 数 量 只 有 1 个 的 不 合 法 情 况 。 输出时下标可从1开始两两输出,可以直接过滤掉数组中的元素数量只有1个的不合法情况。 11

代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
  
using namespace std;
  
const int N=2e5+10;

int primes[N],cnt;
bool st[N];
bool vis[N];

void get_prime(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) primes[cnt++]=i;
        for(int j=0;primes[j]*i<=n;j++)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int main()
{
    get_prime(2e5);
    
    int T,n;
    cin>>T;
    while(T--)
    {
        vector<int> res[N];
        scanf("%d",&n);
        for(int i=0;i<=n;i++) vis[i]=false;
        
        int sum=0;
        for(int i=cnt-1;i>0;i--)
        {
            int p=primes[i];
            for(int j=p;j<=n;j+=p)
                if(!vis[j]) 
                {
                    res[i].push_back(j);
                    vis[j]=true;
                }
                
            int S=res[i].size();
            if(S>1 && S&1) 
            {
                int tmp=res[i][1];
                res[i].erase(res[i].begin()+1);
                vis[2*p]=false;
                S--;
            }
            sum+=S/2;
        }
        
        for(int i=2;i<=n;i+=2)
            if(!vis[i])
                res[0].push_back(i);
        
        sum+=res[0].size()/2;
        
        printf("%d\n",sum);
        for(int i=0;i<cnt;i++)
        {
            int t=res[i].size();
            for(int j=1;j<t;j+=2) printf("%d %d\n",res[i][j-1],res[i][j]);
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值