알고리즘 문제 해결: 서브태스크별 맞춤 전략

모듈러 거듭제곱 (Subtask 1-5)

첫 번째부터 다섯 번째 서브태스크는 주어진 모듈러 값 \(P\)에 대해 \(19^x \pmod P\)를 계산하는 문제입니다. 각 서브태스크별로 \(P\) 값의 특성이 달라지며, 이에 따라 접근 방식이 변화합니다.

Subtask 1-3: \(P=998244353\) 고정

이 서브태스크는 \(P=998244353\)로 고정된 경우입니다. 지수 \(x\)의 크기가 매우 클 수 있으므로, 페르마의 소정리(\(a^{P-1} \equiv 1 \pmod P\) for prime \(P\) and \(P\)의 배수가 아닌 \(a\))를 활용하여 지수를 \(P-1\)로 나눈 나머지로 변환해야 합니다. 즉, \(x\)를 입력받을 때부터 \(P-1\)로 나눈 나머지 값을 취하는 전략이 필요합니다. 이는 일반적인 모듈러 거듭제곱(Modular Exponentiation) 함수를 통해 효율적으로 계산할 수 있습니다.

Subtask 4: 미지의 작은 \(P\) 탐색

네 번째 서브태스크는 \(19^x \pmod P\)를 계산해야 하지만, 모듈러 \(P\) 값이 주어지지 않습니다. 출력값의 범위가 대략 \(10^6\) 정도임을 단서로 활용할 수 있습니다. 주어진 첫 번째 입력값과 그에 대한 출력값을 사용하여 \(1\)부터 \(10^6\)까지의 모든 정수를 \(P\)로 가정하고 모듈러 거듭제곱을 역으로 계산해보면, 실제 모듈러 값 \(P=1145141\)을 찾아낼 수 있습니다.

Subtask 5: 미지의 큰 \(P\) 탐색

다섯 번째 서브태스크는 앞서와 동일하게 \(19^x \pmod P\)를 구하지만, 모듈러 \(P\)가 \(5 \times 10^{18}\) 규모로 매우 크기 때문에 단순 브루트 포스는 어렵습니다. 여기서 관찰을 통해 해결 방법을 찾을 수 있습니다. 입력값 \(x\)들 중 차이가 \(k\)인 두 쌍을 찾습니다. 예를 들어, \(x_1\)과 \(x_2 = x_1 + 2\)가 주어지고 각각의 출력값이 \(A\)와 \(B\)라고 가정해 봅시다.

이는 다음 관계를 만족합니다:

\(A \cdot 19^2 \equiv B \pmod P\)

이 식을 변형하면, \(A \cdot 361 - B = nP\) (여기서 \(n\)은 정수) 형태가 됩니다.

좌변 \(A \cdot 361 - B\)는 매우 큰 값이 될 수 있으며, 일반적인 \(long\ long\) 자료형으로는 오버플로가 발생할 가능성이 있습니다. 이때는 \(long\ double\)을 사용하여 근사치를 계산하거나, 오버플로를 방지하는 큰 숫자 곱셈 함수를 사용해야 합니다. 계산된 값 \(A \cdot 361 - B\)는 \(P\)의 배수이므로, \(P\)는 해당 값의 약수 중 하나가 됩니다. \(n\) 값이 비교적 작은 범위(예: 100-200)에 있음을 확인하고, 근사치 \((A \cdot 361 - B) / n\) 주변의 숫자들을 시도해 보며 모듈러 값이 올바른지 검증합니다. 이 과정을 통해 실제 모듈러 \(P=5211600617818708273\)를 찾을 수 있습니다.

정수 오버플로 및 순환 주기 탐색 (Subtask 6-7)

여섯 번째와 일곱 번째 서브태스크는 \(19^x \pmod{998244353}\)를 구하는 것이 아니라, \(int\)형 변수를 사용하여 반복적으로 \(19\)를 곱하고 모듈러 연산을 수행할 때 발생하는 \(int\)형 오버플로를 그대로 반영한 결과를 요구합니다. 즉, 중간 계산 과정에서 오버플로가 발생하더라도 이를 무시하고 다음 계산에 사용하는 방식입니다. 다음 의사 코드와 같습니다.

val = (int)((unsigned int)val * 19) % 998244353;

여섯 번째 서브태스크는 \(x\) 값이 작으므로 위 연산을 직접 반복하여 계산할 수 있습니다.

하지만 일곱 번째 서브태스크는 \(x\)가 매우 커서 직접 반복하는 것은 불가능합니다. 이러한 형태의 순환식은 결국 특정 시점부터 반복되는 순환 주기를 갖게 됩니다. 예를 들어, \(x=55246\) 이후부터 \(45699\)개의 값이 순환하는 패턴을 발견할 수 있습니다. 충분히 많은 횟수(예: \(10^6\)회)를 미리 계산하여 순환 시작점과 주기를 파악한 후, \(x\)가 클 경우 이 순환 패턴을 이용하여 결과를 얻을 수 있습니다.

원시근 탐색 (Subtask 14-16)

열네 번째부터 열여섯 번째 서브태스크는 주어진 범위 \(l\)부터 \(r\) 사이의 정수 중에서 모듈러 \(P\)에 대한 원시근(Primitive Root)을 찾는 문제입니다.

Subtask 14: 고정된 \(P=998244353\)

\(P=998244353\)는 소수이며, 오일러 피 함수 \(\varphi(P) = P-1 = 998244352 = 2^{23} \times 7 \times 17\)입니다. \(\varphi(P)\)의 소인수 개수가 적으므로, 원시근의 정의에 따라 임의의 정수 \(a\)가 원시근인지 판별하기 위해 \(\varphi(P)\)의 모든 소인수 \(p_i\)에 대해 \(a^{(\varphi(P)/p_i)} \not\equiv 1 \pmod P\)를 만족하는지 직접 검사할 수 있습니다.

Subtask 15: 고정된 \(P=13123111\) (많은 소인수)

이 경우 \(P=13123111\)는 소수이며, \(\varphi(P) = 13123110 = 2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 19 \times 23\)로, 소인수가 많고 검사해야 할 숫자의 범위가 \(10^7\)에 달해 직접 판별하는 것은 비효율적입니다. 이럴 때는 하나의 원시근 \(g\)를 찾아낸 후(예: \(g=6\)), 나머지 원시근들은 \(g^t \pmod P\) 형태임을 활용합니다. 이때 \(g^t\)가 원시근이 되려면 \(t\)와 \(\varphi(P)\)가 서로소여야 합니다. 따라서 \(\varphi(P)\)의 소인수들을 이용하여 \(t\)와 서로소인지 빠르게 판별할 수 있으며, 이 방법으로 모든 원시근을 효율적으로 찾아낼 수 있습니다.

Subtask 16: 미지의 \(P\) 및 원시근 활용

마지막 서브태스크에서는 모듈러 \(P\)가 미지수입니다. 힌트는 \(P\)가 \(10^9\)에서 \(2 \times 10^9\) 사이의 소수임을 알려줍니다. 주어진 원시근이 있다고 가정하고, 해당 범위 내의 모든 수를 탐색하며 소수 여부를 밀러-라빈(Miller-Rabin)으로 검사하고, 후보 \(P\)에 대해 주어진 원시근이 실제로 원시근이 될 수 있는지 확인합니다. 예를 들어, \(g^{(P-1)/2} \not\equiv 1 \pmod P\)와 같은 조건을 활용하여 가능한 \(P\)를 추려낼 수 있습니다. 이 과정을 통해 \(P=1515343657\)와 같은 값을 찾아낼 수 있으며, 이후에는 열네 번째 서브태스크와 동일한 방식으로 원시근을 찾습니다.

소수 판별 (Subtask 8-10)

여덟 번째부터 열 번째 서브태스크는 주어진 범위 \(l\)부터 \(r\) 사이의 각 숫자가 소수인지 판별하는 문제입니다.

작은 범위의 숫자들에 대해서는 에라토스테네스의 체(Sieve of Eratosthenes)와 같은 선형 체(Linear Sieve) 알고리즘을 사용하여 효율적으로 소수를 판별할 수 있습니다.

하지만 범위가 넓어지거나 숫자의 크기가 커질 경우에는 밀러-라빈(Miller-Rabin) 소수 판별법이 유용합니다. 밀러-라빈은 확률적 알고리즘으로, \(O((\log N)^3)\) 정도의 시간 복잡도로 숫자가 소수인지 판별할 수 있으며, 테스트 횟수 \(k\)에 따라 오판 확률이 \(1/4^k\)로 줄어듭니다. 일반적으로 몇 개의 고정된 밑(base)을 사용하여 테스트하면 매우 높은 정확도로 실용적인 시간 안에 소수 여부를 판단할 수 있습니다. 이 문제에서는 대략 2-3개의 밑으로도 충분히 통과할 수 있습니다.

뫼비우스 함수 계산 (Subtask 11-13)

열한 번째부터 열세 번째 서브태스크는 주어진 범위 \(l\)부터 \(r\) 사이의 각 숫자에 대한 뫼비우스 함수(\(\mu\)) 값을 계산하는 문제입니다.

작은 범위에 대해서는 선형 체를 통해 \(\mu\) 값을 미리 계산할 수 있지만, 최대 \(10^{18}\)에 달하는 큰 숫자에 대해서는 직접 체를 적용하기 어렵습니다. 이러한 큰 숫자에 대한 뫼비우스 함수를 계산하기 위해서는 \(\sqrt{N_{max}}\) (예: \(10^{18}\)의 경우 \(10^9\))까지의 소수들을 미리 체로 걸러내는 전략이 필요합니다.

주어진 숫자 \(N\)을 \(10^9\) 이하의 소인수들로 나눈 후 남은 몫을 \(val\)이라고 하겠습니다. 이때 \(val\)은 다음 세 가지 경우 중 하나에 해당합니다. (단, \(val > 1\))

  1. \(val\)이 소수인 경우: \(10^9\)보다 큰 소수는 많지 않으므로, 밀러-라빈 소수 판별법으로 \(val\)이 소수인지 확인합니다. 이 경우 \(\mu\) 값에 추가적으로 \(-1\)이 곱해집니다.
  2. \(val\)이 두 개의 서로 다른 소수의 곱인 경우: \(val = p_1 \cdot p_2\) (여기서 \(p_1, p_2 > 10^9\)이며 \(p_1 \neq p_2\))입니다. 이 경우 \(\mu\) 값은 변화가 없습니다 (\((-1) \times (-1) = 1\) 효과).
  3. \(val\)이 소수의 제곱인 경우: \(val = p^2\) (여기서 \(p > 10^9\))입니다. 이 경우 뫼비우스 함수 정의에 따라 \(\mu(N)\)은 \(0\)이 됩니다. 제곱근을 이용한 판별로 확인할 수 있습니다.

이러한 방식으로 \(l\)부터 \(r\)까지의 모든 숫자에 대해 뫼비우스 함수 값을 계산할 수 있습니다. 특히, 제곱 인수를 포함하는 숫자는 \(\mu\) 값이 \(0\)이 되므로, 이를 정확히 처리하는 것이 중요합니다.

#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include <algorithm>
#include <cmath>
#lt;cstring> // For memset
#include <cstdio>  // For getchar

// 타입 별칭 정의
using long_long = long long;
using unsigned_long_long = unsigned long long;

// --- 모듈러 연산 헬퍼 함수 ---

// 모듈러 덧셈: (a + b) % p
inline void add_mod(unsigned_long_long &a, unsigned_long_long b, unsigned_long_long p) {
    a += b;
    if (a >= p) a -= p;
}

// 모듈러 곱셈: (a * b) % p (비트 연산을 이용한 효율적인 구현)
unsigned_long_long modular_multiply(unsigned_long_long factor_a, unsigned_long_long factor_b, unsigned_long_long modulus) {
    unsigned_long_long result = 0;
    factor_a %= modulus;
    factor_b %= modulus;
    while (factor_b > 0) {
        if (factor_b & 1) {
            add_mod(result, factor_a, modulus);
        }
        add_mod(factor_a, factor_a, modulus);
        factor_b >>= 1;
    }
    return result;
}

// 모듈러 거듭제곱: (base^exp) % p
unsigned_long_long modular_power(unsigned_long_long base, unsigned_long_long exponent, unsigned_long_long modulus) {
    unsigned_long_long result = 1;
    base %= modulus;
    while (exponent > 0) {
        if (exponent & 1) result = modular_multiply(result, base, modulus);
        base = modular_multiply(base, base, modulus);
        exponent >>= 1;
    }
    return result;
}

// 큰 지수 X를 (모듈러-1)로 나눈 나머지를 읽는 함수
unsigned_long_long read_large_exponent_mod(unsigned_long_long reduced_modulus) {
    unsigned_long_long value = 0;
    char char_input;
    while (!isdigit(char_input = getchar())); // 숫자 문자가 나올 때까지 건너뛴다
    while (isdigit(char_input)) {
        value = (value * 10 + (char_input - '0'));
        if (value >= reduced_modulus) { // 읽는 도중 모듈러 연산 적용
            value %= reduced_modulus;
        }
        char_input = getchar();
    }
    return value;
}

// --- Subtask 1-5 해결 로직: 다양한 P에 대한 모듈러 거듭제곱 ---
void solve_modular_exponentiation(unsigned_long_long current_modulus_P) {
    int num_queries;
    std::cin >> num_queries;
    for (int i = 0; i < num_queries; ++i) {
        unsigned_long_long exponent_val = read_large_exponent_mod(current_modulus_P - 1); // X mod (P-1) 읽기
        std::cout << modular_power(19, exponent_val, current_modulus_P) << "\n";
    }
}

// --- Subtask 6-7 해결 로직: 정수 오버플로 및 순환 주기 감지 ---
void solve_int_overflow_cycle() {
    int num_queries;
    std::cin >> num_queries;

    // 순환 주기 감지를 위한 수열 미리 계산
    const int CYCLE_START_INDEX = 55246; // 순환 시작점
    const int CYCLE_LENGTH = 45699;      // 순환 길이
    std::vector<int> sequence_cache(CYCLE_START_INDEX + CYCLE_LENGTH + 5); 

    sequence_cache[0] = 1; // 19^0
    for (int i = 1; i < sequence_cache.size(); ++i) {
        // 문제의 요구사항인 int 오버플로 시뮬레이션:
        // (unsigned int)로 캐스팅하여 32비트 unsigned 정수 범위 내에서 곱셈을 수행하면 오버플로 시 정확히 2^32로 나눈 나머지가 됨
        unsigned int intermediate_product = (unsigned int)sequence_cache[i-1] * 19;
        sequence_cache[i] = (int)(intermediate_product % 998244353);
    }

    for (int q = 0; q < num_queries; ++q) {
        long_long input_exponent_val;
        std::cin >> input_exponent_val;
        if (input_exponent_val < sequence_cache.size()) {
            std::cout << sequence_cache[input_exponent_val] << "\n";
        } else {
            // 순환 주기 활용: (입력 지수 - 순환 시작점) % 순환 길이 + 순환 시작점
            long_long effective_index = (input_exponent_val - CYCLE_START_INDEX) % CYCLE_LENGTH + CYCLE_START_INDEX;
            std::cout << sequence_cache[effective_index] << "\n";
        }
    }
}

// --- Subtask 14-16 해결 로직: 원시근 탐색 ---
namespace PrimitiveRootFinder {
    // int 타입을 위한 모듈러 거듭제곱 함수
    int modular_power_int(int base, int exponent, int modulus) {
        int result = 1;
        base %= modulus;
        while (exponent > 0) {
            if (exponent & 1) result = (long_long)result * base % modulus;
            base = (long_long)base * base % modulus;
            exponent >>= 1;
        }
        return result;
    }

    // 숫자의 모든 고유 소인수를 구하는 함수
    std::vector<int> get_distinct_prime_factors(int number) {
        std::vector<int> factors;
        for (int i = 2; (long_long)i * i <= number; ++i) {
            if (number % i == 0) {
                factors.push_back(i);
                while (number % i == 0) {
                    number /= i;
                }
            }
        }
        if (number > 1) {
            factors.push_back(number);
        }
        return factors;
    }

    // 'num'이 'modulus'의 원시근인지 확인하는 함수 (modulus는 소수여야 함)
    bool check_primitive_root(int candidate_num, int modulus, const std::vector<int>& prime_factors_of_phi) {
        if (candidate_num <= 1 || std::gcd(candidate_num, modulus) != 1) return false;

        int phi_val = modulus - 1; // 소수 P에 대해 phi(P) = P-1

        for (int factor : prime_factors_of_phi) {
            if (modular_power_int(candidate_num, phi_val / factor, modulus) == 1) {
                return false; // 원시근 조건 불만족
            }
        }
        return true; // 원시근
    }

    // 고정된 P에 대해 L부터 R까지 원시근을 찾는 함수
    void solve_for_fixed_P(int lower_bound, int upper_bound, int current_P) {
        std::vector<int> phi_factors = get_distinct_prime_factors(current_P - 1);
        for (int i = lower_bound; i <= upper_bound; ++i) {
            std::cout << (check_primitive_root(i, current_P, phi_factors) ? 'g' : '.');
        }
        std::cout << "\n";
    }

    // P=13123111과 같이 소인수가 많은 경우, 모든 원시근을 미리 계산하여 출력하는 함수
    void solve_for_many_factors_P(int current_P) {
        std::vector<bool> is_primitive_root_flags(current_P, false); 
        
        // 문제의 풀이에서 g=6을 사용하여 순환하는 방식으로 원시근을 탐색했음.
        // 6^t가 원시근이 되는 t는 phi(P)와 서로소인 경우.
        int current_power_val = 6; // 6^1부터 시작
        int exponent_count = 0;
        int phi_val = current_P - 1;
        std::vector<int> phi_factors_for_exponent = get_distinct_prime_factors(phi_val);

        do {
            exponent_count++;
            bool is_exponent_coprime = true;
            for (int factor : phi_factors_for_exponent) {
                if (exponent_count % factor == 0) {
                    is_exponent_coprime = false;
                    break;
                }
            }
            if (is_exponent_coprime) {
                is_primitive_root_flags[current_power_val] = true;
            }
            current_power_val = (long_long)current_power_val * 6 % current_P;
        } while (current_power_val != 6); // 6^t가 다시 6^1이 될 때까지 반복

        for (int i = 1; i < current_P; ++i) {
            std::cout << (is_primitive_root_flags[i] ? 'g' : '.');
        }
        std::cout << "\n";
    }
} // namespace PrimitiveRootFinder

// --- Subtask 8-10 해결 로직: 밀러-라빈 소수 판별 ---
namespace PrimalityTester {
    // long long 타입을 위한 모듈러 곱셈: (a * b) % p
    // (a*b)가 ULLONG_MAX를 초과할 수 있는 경우 long double을 사용하여 오버플로를 방지
    long_long modular_multiply_ll(long_long factor_a, long_long factor_b, long_long modulus) {
        factor_a %= modulus;
        factor_b %= modulus;
        long_long result_val = factor_a * factor_b - (long_long)((unsigned long long)((long double)factor_a * factor_b / modulus) * modulus);
        if (result_val < 0) {
            result_val += modulus;
        } else if ((unsigned long long)result_val >= (unsigned long long)modulus) {
            result_val -= modulus;
        }
        return result_val;
    }

    // long long 타입을 위한 모듈러 거듭제곱: (base^exp) % p
    long_long modular_power_ll(long_long base, long_long exponent, long_long modulus) {
        long_long result = 1;
        base %= modulus;
        while (exponent > 0) {
            if (exponent & 1) result = modular_multiply_ll(result, base, modulus);
            base = modular_multiply_ll(base, base, modulus);
            exponent >>= 1;
        }
        return result;
    }

    // 밀러-라빈 소수 판별 테스트
    bool miller_rabin_test(long_long n) {
        if (n < 2) return false;
        if (n == 2 || n == 3) return true;
        if (n % 2 == 0) return false;

        long_long d = n - 1;
        int s = 0;
        while (d % 2 == 0) {
            d /= 2;
            s++;
        }

        // 특정 범위의 N에 대해 충분한 검증을 제공하는 테스트 밑(bases) 집합
        // N이 3.8 * 10^16 이하일 때 {2, 3, 5, 7, 11, 13, 17, 19, 23}로 충분.
        // long long 범위 전체를 커버하려면 더 많은 밑이 필요.
        long_long test_bases[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
        
        for (long_long a : test_bases) {
            if (a >= n) break; 
            long_long x = modular_power_ll(a, d, n);
            if (x == 1 || x == n - 1) continue;

            bool is_prime_witness = false;
            for (int r = 1; r < s; ++r) {
                x = modular_multiply_ll(x, x, n);
                if (x == n - 1) {
                    is_prime_witness = true;
                    break;
                }
            }
            if (!is_prime_witness) return false; // 'a'는 합성수임을 증명하는 증인
        }
        return true; // 매우 높은 확률로 소수
    }

    void solve_primality_queries() {
        int num_queries;
        std::cin >> num_queries;
        while (num_queries--) {
            long_long l_range, r_range;
            std::cin >> l_range >> r_range;
            for (long_long i = l_range; i <= r_range; ++i) {
                std::cout << (miller_rabin_test(i) ? 'p' : '.');
            }
            std::cout << "\n";
        }
    }
} // namespace PrimalityTester

// --- Subtask 11-13 해결 로직: 뫼비우스 함수 계산 ---
namespace MobiusFunctionCalculator {
    const int SMALL_PRIME_SIEVE_LIMIT = 1000000;
    std::vector<int> smallest_prime_factor_sieve(SMALL_PRIME_SIEVE_LIMIT + 1);
    std::vector<int> prime_list;

    void generate_small_primes() {
        std::iota(smallest_prime_factor_sieve.begin(), smallest_prime_factor_sieve.end(), 0); // 초기화: spf[i]=i
        for (int i = 2; i <= SMALL_PRIME_SIEVE_LIMIT; ++i) {
            if (smallest_prime_factor_sieve[i] == i) { // i가 소수인 경우
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (p > smallest_prime_factor_sieve[i] || (long_long)i * p > SMALL_PRIME_SIEVE_LIMIT) {
                    break;
                }
                smallest_prime_factor_sieve[i * p] = p; // 합성수의 가장 작은 소인수 업데이트
            }
        }
    }

    // 숫자가 특정 한계보다 큰 소수의 제곱인지 확인 (예: 10^6보다 큰 소수 p에 대해 p^2)
    bool is_large_prime_squared(long_long n_val) {
        if (n_val <= 1) return false;
        long_long root_n = round(sqrt(n_val));
        if (root_n * root_n == n_val) {
            // 제곱근이 소수이고, 작은 소수 체 한계를 넘는 큰 소수인지 확인
            return PrimalityTester::miller_rabin_test(root_n) && root_n > SMALL_PRIME_SIEVE_LIMIT;
        }
        return false;
    }

    void solve_mobius_queries() {
        generate_small_primes(); // 작은 소수 리스트 한 번만 생성

        int num_queries;
        std::cin >> num_queries;
        while (num_queries--) {
            long_long lower_bound, upper_bound;
            std::cin >> lower_bound >> upper_bound;

            // 뫼비우스 값과 남은 인수들을 저장할 배열
            std::vector<int> current_mobius_values(upper_bound - lower_bound + 1);
            std::vector current_remaining_factors(upper_bound - lower_bound + 1); 
            
            for (long_long i = lower_bound; i <= upper_bound; ++i) {
                current_mobius_values[i - lower_bound] = 1; // 초기 μ(N) = 1 (모든 인수가 1일 때)
                current_remaining_factors[i - lower_bound] = i; // 남은 인수는 자기 자신부터 시작
            }

            // 작은 소수들을 사용하여 범위 내의 숫자들에 대해 뫼비우스 값 처리
            for (int p_val : prime_list) {
                long_long p_squared = (long_long)p_val * p_val;
                
                // p^2으로 나누어지는 수들은 μ=0
                long_long start_for_p_sq = (lower_bound + p_squared - 1) / p_squared * p_squared;
                for (long_long j = start_for_p_sq; j <= upper_bound; j += p_squared) {
                    current_mobius_values[j - lower_bound] = 0;
                }

                // p로 나누어지는 수들은 μ에 -1을 곱함
                long_long start_for_p = (lower_bound + p_val - 1) / p_val * p_val;
                for (long_long j = start_for_p; j <= upper_bound; j += p_val) {
                    if (current_mobius_values[j - lower_bound] != 0) { // 이미 0이 된 수는 건너뛴다
                        current_mobius_values[j - lower_bound] *= -1;
                        // 현재까지 처리되지 않은 남은 인수를 업데이트
                        current_remaining_factors[j - lower_bound] /= p_val;
                    }
                }
            }

            // 작은 소인수 분해 후 남은 인수에 대해 처리
            for (long_long i = lower_bound; i <= upper_bound; ++i) {
                long_long offset_idx = i - lower_bound;
                if (current_mobius_values[offset_idx] == 0) {
                    std::cout << '0';
                    continue;
                }

                long_long remaining_factor_val = current_remaining_factors[offset_idx];
                if (remaining_factor_val == 1) { // 모든 소인수가 SMALL_PRIME_SIEVE_LIMIT 이하
                    std::cout << (current_mobius_values[offset_idx] == 1 ? '+' : '-');
                } else {
                    // 남은 인수가 1보다 큰 경우: 큰 소수, 두 개의 큰 소수 곱, 또는 큰 소수의 제곱
                    if (PrimalityTester::miller_rabin_test(remaining_factor_val)) { // Case 1: 남은 인수가 큰 소수
                        std::cout << (current_mobius_values[offset_idx] * -1 == 1 ? '+' : '-');
                    } else if (is_large_prime_squared(remaining_factor_val)) { // Case 3: 남은 인수가 큰 소수의 제곱
                        std::cout << '0';
                    } else { // Case 2: 남은 인수가 두 개의 서로 다른 큰 소수의 곱
                             // (remaining_factor_val이 소수가 아니고, 소수의 제곱도 아니면,
                             // 문제의 범위 제한(10^18)과 sieve_limit(10^6)에 따라 두 개의 다른 소수 곱일 수밖에 없음)
                        std::cout << (current_mobius_values[offset_idx] == 1 ? '+' : '-');
                    }
                }
            }
            std::cout << "\n";
        }
    }
} // namespace MobiusFunctionCalculator


// --- 메인 함수: 입력 태그에 따라 서브태스크별 로직 실행 ---
int main() {
    std::ios_base::sync_with_stdio(false); // C++ 스트림과 C 스트림 동기화 해제
    std::cin.tie(NULL); // cin, cout 묶음 해제 (입출력 속도 향상)
    srand(time(0)); // 밀러-라빈 테스트 등에서 필요할 수 있는 난수 생성기 시드 설정

    char input_tag_str[15];
    std::cin >> input_tag_str; // 서브태스크 식별자 문자열 읽기

    if (strcmp(input_tag_str, "1_998244353") == 0) {
        solve_modular_exponentiation(998244353ULL);
    } else if (strcmp(input_tag_str, "1?") == 0) {
        solve_modular_exponentiation(1145141ULL);
    } else if (strcmp(input_tag_str, "1?+") == 0) {
        solve_modular_exponentiation(5211600617818708273ULL);
    } else if (strcmp(input_tag_str, "1wa_998244353") == 0) {
        solve_int_overflow_cycle();
    } else if (strcmp(input_tag_str, "2g") == 0) {
        int num_queries;
        std::cin >> num_queries;
        while (num_queries--) {
            int l_val, r_val, p_val;
            std::cin >> l_val >> r_val >> p_val;
            if (p_val == 998244353) {
                PrimitiveRootFinder::solve_for_fixed_P(l_val, r_val, p_val);
            } else { // 문제의 맥락상 P=13123111을 의미
                PrimitiveRootFinder::solve_for_many_factors_P(p_val); 
            }
        }
    } else if (strcmp(input_tag_str, "2g+") == 0) {
        int num_queries;
        std::cin >> num_queries;
        for (int q_idx = 0; q_idx < num_queries; ++q_idx) {
            int l_val, r_val, p_val;
            std::cin >> l_val >> r_val;
            if (q_idx < num_queries - 1) { // 마지막 쿼리가 아닌 경우 P를 입력받음
                std::cin >> p_val;
            } else { // 마지막 쿼리인 경우 P는 1515343657로 고정
                p_val = 1515343657; 
            }
            PrimitiveRootFinder::solve_for_fixed_P(l_val, r_val, p_val);
        }
    } else if (strcmp(input_tag_str, "2p") == 0) {
        PrimalityTester::solve_primality_queries();
    } else if (strcmp(input_tag_str, "2u") == 0) {
        MobiusFunctionCalculator::solve_mobius_queries();
    }

    return 0;
}

태그: 모듈러_거듭제곱 밀러_라빈 원시근 뫼비우스_함수 정수론

7월 18일 19:18에 게시됨