AT_abc215_d 题解

发布时间 2023-07-24 14:29:36作者: So_noSlack

洛谷链接&Atcoder 链接

本篇题解为此题较简单做法较少码量,并且码风优良,请放心阅读。

题目简述

给定 \(N\)\(M\) 及含有 \(N\) 个整数的序列 \(A\)

\(1 \sim M\) 中与所有 \(a_i\) 均互质的整数及个数。

思路

首先说一下最开始的想法

直接暴力枚举 \(1 \sim M\) 的数,再分别枚举每个数与 \(a_i\) 是否互质,时间复杂度 \(O(N \times M)\) 左右,一看数据范围直接爆炸提交结果就可想而知了。

TLE \(0\) 分代码如下,因为是暴力就不写注释了:

#include<iostream>
#include<cmath>
using namespace std;

int n, m, a[100005], num[100005], ans = 0;

int gcd(int x, int y) {
    return (y == 0 ? x : gcd(y, x % y));
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= m; i ++) {
        bool flag = false;
        for(int j = 1; j <= n; j ++) 
            if(gcd(a[j], i) != 1) { flag = true; break; }
        if(!flag) ans ++, num[ans] = i;
    }
    cout << ans << endl;
    for(int i = 1; i <= ans; i ++) cout << num[i] << endl;
    return 0;
}

接下来让我们进行一点小优化。

既然对于 \(1 \sim M\) 每个数进行暴力枚举会超时,那么就需要在输入时进行一些标记,经过一些尝试后发现,可以在输入时记录每个数的因数并标记在 \(flag\) 数组中,看到 \(a_i\) 的数据范围后发现可以用桶存。因为 \(a_i\) 的因数会重复,所以在输入后需要另开一个 \(vul\) 存储序列 \(A\)所有因数,不必用桶存。

如:

#define MAXN 1000000

void f(int x) {
    flag[x] = true;
    for(int i = 2; i * i <= x; i ++) 
        if(x % i == 0) flag[i] = flag[x / i] = true;
    return;
}
                             
int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) { cin >> x; f(x); }
    for(int i = 2; i <= MAXN; i ++) 
        if(flag[i]) vul[++ t] = i;
    return 0;
}

接着就可以不那么暴力的枚举了。

首先枚举 \(1 \sim M\) 是一定的,但在第二层循环中仅需枚举当前 \(i\) 是否为 \(vul\) 中因数的倍数,如果是直接跳出当前循环,如果跑了一遍 \(vul\) 发现当前 \(i\) 不是任何 \(vul\) 中因数的倍数,则记录答案至数组 \(num\) 中即可。

经过以上一点小优化,很容易即可写出代码

#include<iostream>
#include<cmath>
using namespace std;

#define MAXN 1000000 // 最大值

int n, m, x, t, vul[MAXN + 5], num[MAXN + 5], ans, j;
bool flag[MAXN + 5];

void f(int x) {
    flag[x] = true;
    for(int i = 2; i * i <= x; i ++) // 遍历 x 求因数
        if(x % i == 0) flag[i] = flag[x / i] = true; // 标记 x 的因数
    return;
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) { cin >> x; f(x); } // 输入中处理
    for(int i = 2; i <= MAXN; i ++) 
        if(flag[i]) vul[++ t] = i;
    for(int i = 1; i <= m; i ++) {
        for(j = 1; j <= t; j ++)
            if(i % vul[j] == 0) break; // 不满足情况直接跳出循环
        if(j == t + 1) num[++ ans] = i; // 满足情况记录答案
    }
    cout << ans << endl; // 输出个数
    for(int i = 1; i <= ans; i ++) cout << num[i] << endl; // 输出答案
    return 0;
}

提交记录

\[\text{The End!!!} \]