[Daimayuan] 互质(C++,数学)

题目描述

给你一个包含n个正整数的序列 A = ( A 1 , A 2 , . . . , A n ) A=(A1,A2,...,An) A=(A1,A2,...,An),找到 [ 1 , m ] [1,m] [1,m]中每一个满足下列条件的 k k k

g c d ( A i , k ) = 1 gcd(A_i,k)=1 gcd(Ai,k)=1, 1 ≤ i ≤ n 1≤i≤n 1in

输入描述

第一行输入两个整数 n n n, m m m 第二行输入 n n n个整数代表序列 A A A

输出描述

第一行输出一个整数代表满足条件的k的数量 接下里每一行输出一个整数,代表一个满足条件的 k k k

样例输入
3 12
6 1 5
样例输出
3
1
7
11
数据范围

1 ≤ n , m ≤ 100000 1≤n,m≤100000 1n,m100000

1 ≤ a i ≤ 100000 1≤a_i≤100000 1ai100000

解题思路

本来这道题是想用欧拉筛做的(只考虑部分数情况下的“质数”),但是并不是质数就符合条件,因为互质要求是最大公约数为 1 1 1,而质数只是不能分解而已。

对于互质的数 a , b a,b a,b g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,也就是说互质的数不能有公因数。

我们将这个问题优化一下,互质的数不能有公共质因数(因为任何合数都可以分解为质数的乘积)。

那么现在我们的思路就是将所有输入分解为质因数,然后利用分解得到的质因数筛选符合条件的 k k k

接下来是代码实现部分:

首先,我们需要知道如何将输入分解为质因数

int num;
for (int i = 0; i < n; i++) {
	cin >> num;
	while (num != 1) {
		fliter[maxPrime[num]] = true;
		num /= maxPrime[num];
	}	
}

其实,就是将 n u m num num不断除以其最大质因数。

那么问题出现了,如何求任意数的最大质因数?这里采用埃氏筛的算法(简而言之就是用质数的倍数筛去非质数)的变形:

memset(isPrime + 1, true, sizeof(bool) * max_n);//初始化,默认所有数均为质数
maxPrime[1] = 1;//对1进行特殊处理
isPrime[1] = false;
for (int i = 2; i <= max_n; i++) {
	if (!isPrime[i]) continue;//非质数
	maxPrime[i] = i;
	for (int j = 2 * i; j <= max_n; j += i) {
		isPrime[j] = false;
		maxPrime[j] = i;
	}
}

筛子做好了,接下来就是筛选 1 1 1~ M M M的所有数了:

vector<int>v;
v.push_back(1);//对1进行特殊处理
for (int i = 2; i <= m; i++) {
    bool flag = true;
    int temp = i;
    while (temp != 1) {
        if (fliter[maxPrime[temp]]) {
            flag = false;
            break;
        }
        temp /= maxPrime[temp];
    }
    if (flag) v.push_back(i);
}

在题目之初提到的问题就是筛选一个 k k k,既需要考虑 k k k能否分解为小于 k k k的数,又要考虑 k k k之后的数是否为 k k k的倍数。

但是现在我们将序列 A A A中的元素分解了,就没有这个问题了,只需要考虑 k k k能否分解即可。

最后,AC代码如下:

#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
const int max_n = 100000;

bool isPrime[max_n + 1], fliter[max_n + 1];     //质数数组, 筛子
int maxPrime[max_n + 1];                        //最大质因数

int main() {
    //prepare
    memset(isPrime + 1, true, sizeof(bool) * max_n);//初始化,默认所有数均为质数
    maxPrime[1] = 1;//对1进行特殊处理
    isPrime[1] = false;
    for (int i = 2; i <= max_n; i++) {
        if (!isPrime[i]) continue;//非质数
        maxPrime[i] = i;
        for (int j = 2 * i; j <= max_n; j += i) {
            isPrime[j] = false;
            maxPrime[j] = i;
        }
    }

    //input
    int n, m, num;
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num;
        while (num != 1) {
            fliter[maxPrime[num]] = true;
            num /= maxPrime[num];
        }
    }

    //fliter
    vector<int>v;
    v.push_back(1);//对1进行特殊处理
    for (int i = 2; i <= m; i++) {
        bool flag = true;
        int temp = i;
        while (temp != 1) {
            if (fliter[maxPrime[temp]]) {
                flag = false;
                break;
            }
            temp /= maxPrime[temp];
        }
        if (flag) v.push_back(i);
    }

    //output
    cout << v.size() << endl;
    for (auto iter : v) {
        cout << iter << endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值