Coding Test/BaekJoon_C++

백준 1016 <제곱 ㄴㄴ 수> C++

JunOnJuly 2024. 5. 1. 18:03
728x90

https://www.acmicpc.net/problem/1016


제곱수의 배수가 아닌 수를 찾는 문제입니다. 다만 제한이 상당히 빡빡시간을 최대한 단축해야 통과할 수 있습니다.

특정 범위의 수들을 제곱수로 체를 칠텐데, 우리는 어떠한 범위에서 A 의 배수들을 제거하면 NxA 의 배수들도 모두 없어짐을 알고 있습니다. NxA 의 배수들은 A 의 배수이기도 하기 때문입니다.

A^2 으로 체를 친다는 말은 (N*A)^2 은 생각하지 않아도 된다는 말과 같습니다. 어차피 A^2 을 고려하며 모두 제거되었기 때문입니다. 즉 고려해야하는 수 A어떤 수의 배수가 아닌 수, 즉 소수임을 알 수 있습니다. 그렇기에 문제를 해결하는 프로세스는 다음과 같습니다.

 

1. 최댓값 MAX 의 제곱근 이하의 소수를 모두 구한다. ( MAX 이하의 소수의 제곱수를 모두 구하기 위해 )

2. 1 에서 구해진 소수들을 제곱한다.

3. 2 에서 구해진 소수의 제곱수들로 특정 범위를 체친다. ( 배수가 아닌 수를 찾는다 )


#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

// 소수를 찾는 함수
vector<long long int> search_unique(long long int& max) {
    // 소수 목록
    vector<long long int> uniques;
    // 수 체크를 위한 벡터
    vector<bool> nums(max, true);
    // 순회
    for (long long int i = 2; i <= max; i++) {
        // i 가 체크되어 있지 않으면
        // k 가 소수이면 nk 는 소수가 아님이 자명하므로
        if (nums[i - 1]) {
            uniques.push_back(i);
            for (long long int j = i; j <= max; j += i) {
                // j 가 체크되어 있지 않으면
                if (nums[j - 1]) nums[j - 1] = false;
            }
        }
    }

    return uniques;
}

// 벡터의 제곱수들을 구하는 함수
vector<long long int> squares(vector<long long int>& vec) {
    // 순회하며 제곱
    for (long long int i = 0; i < vec.size(); i++) {
        vec[i] = pow(vec[i], 2);
    }

    return vec;
}

// MAX 이하의 소수의 제곱수를 찾는 함수
vector<long long int> search_under_square(long long int& max) {
    // 소수의 제곱수
    vector<long long int> square_uniques;
    // max 의 제곱근 (long long int)
    long long int sqrt_max = static_cast<long long int>(sqrt(max));
    // max 이하의 소수
    vector<long long int> uniques = search_unique(sqrt_max);
    // 소수의 제곱 벡터

    return squares(uniques);
}

// 특정 수들 체로 특정 범위의 수들을 걸러내는 함수
long long int filter_nums(vector<long long int>& vec, long long int& MIN, long long int& MAX) {
    // 걸러낼 범위의 수들
    vector<bool> nums(MAX - MIN + 1, true);
    // 걸러내기
    for (auto num : vec) {
        // min 보다 큰 최소 배수
        long long int min_num = MIN%num == 0 ? MIN : num * (MIN / num) + num;
        // max 보다 작은 최대 배수
        long long int max_num = MAX%num == 0 ? MAX : num * (MAX / num);
        // 배수 체크
        for (long long int i = min_num; i <= max_num; i += num) nums[i - MIN] = false;
    }

    return count(nums.begin(), nums.end(), true);
}

int main(void) {
    // 입력
    long long int MIN, MAX;
    cin >> MIN >> MAX;
    // MAX 이하의 (소수의 제곱수)들
    auto square_uniques = search_under_square(MAX);
    // 걸러내기
    cout << filter_nums(square_uniques, MIN, MAX);
}

 

728x90