알고리즘/문제 풀이

1016번: 제곱 ㄴㄴ 수

Themion 2021. 12. 5. 22:34

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

 

1016번: 제곱 ㄴㄴ 수

어떤 정수 X가 1보다 큰 제곱수로 나누어 떨어지지 않을 때, 그 수를 제곱ㄴㄴ수라고 한다. 제곱수는 정수의 제곱이다. min과 max가 주어지면, min보다 크거나 같고, max보다 작거나 제곱ㄴㄴ수가 몇

www.acmicpc.net

에라토스테네스의 체를 응용한 문제이다.

에라토스테네스의 체는 1차원 배열의 모든 인덱스에 대해, 소수임을 나타내는 값을 발견하면 그 인덱스에 정수배를 한 모든 인덱스의 값에 소수가 아님을 나타내는 값을 할당한다.

마찬가지로, 이 문제에서는 모든 인덱스에 대해 제곱 ㄴㄴ 수임을 나타내는 값을 발견하면, 그 인덱스에 정수배를 한 모든 인덱스의 값에 제곱 ㄴㄴ 수가 아님을 나타내는 값을 할당한다.

#include <cmath>
#include <cstdio>

#define MAX_IDX 1'000'000

// 해당 인덱스의 수가 제곱 ㄴㄴ 수라면 false, 아니라면 true
bool str[MAX_IDX + 1];

int main() {
    // ret: 입력받은 구간 안의 제곱 ㄴㄴ수의 개수
    int ret = 0;
    // min, max: 입력받을 구간
    // end: 입력받은 구간 안에서 가장 큰 제곱수를 만들 수 있는 수
    // bot: 어느 제곱수 n에 대해 k * n이 구간 안에 들어갈 최소 k값
    long long min, max, end, bot;

    // 구간을 입력받은 뒤 가장 큰 제곱수를 구한다
    scanf("%lld %lld", &min, &max);
    end = (long long)(sqrt(max));

    // 모든 제곱근에 대해
    for (long long i = 2; i <= end; i++) {
        // min값을 i^2로 나눈 값부터 시작해서
        bot = min / (i * i);
        // 해당 값에 i^2를 곱한 값이 min보다 작다면 값을 1 올린다
        while ((bot * i * i) < min) bot += 1;

        // 범위 [bot, top] 안의 모든 수 k에 대해 k*(i^2)는 제곱ㄴㄴ수에서 제외
        for (long long k = bot; k <= max / (i * i); k++) 
            str[k * (i * i) - min] = true;
    }

    // 각 수에 대해 해당 수가 제곱 ㄴㄴ수라면 ret에 1씩 더한다
    for (long long i = 0; i <= max - min; i++) if (!str[i]) ret += 1;
    printf("%d\n", ret);

    return 0;
}