結果

問題 No.97 最大の値を求めるくえり
ユーザー qwewe
提出日時 2025-05-14 13:18:37
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 541 ms / 5,000 ms
コード長 6,703 bytes
コンパイル時間 1,304 ms
コンパイル使用メモリ 99,444 KB
実行使用メモリ 6,272 KB
最終ジャッジ日時 2025-05-14 13:19:30
合計ジャッジ時間 4,093 ms
ジャッジサーバーID
(参考情報)
judge2 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 19
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <cmath> // For std::sqrt and std::ceil

// PRNG implementation (provided in problem statement)
// State variables for the xor128 pseudo-random number generator
unsigned xor128_x = 123456789, xor128_y = 362436069, xor128_z = 521288629, xor128_w = 88675123;

// xor128 function to generate pseudo-random unsigned integers
unsigned xor128() {
    unsigned t = xor128_x ^ (xor128_x << 11);
    xor128_x = xor128_y; xor128_y = xor128_z; xor128_z = xor128_w;
    return xor128_w = xor128_w ^ (xor128_w >> 19) ^ (t ^ (t >> 8));
}

// Define the modulus P as a constant. P = 100003 is given in the problem statement and is prime.
const int P = 100003; 

// Modular exponentiation function: computes (base^exp) % P
// Uses binary exponentiation (also known as exponentiation by squaring) for efficiency.
long long power(long long base, long long exp) {
    long long res = 1; // Initialize result
    base %= P; // Take base modulo P
    while (exp > 0) {
        // If exp is odd, multiply result with base
        if (exp % 2 == 1) res = (res * base) % P;
        // Square the base and take modulo P
        base = (base * base) % P; 
        // Halve the exponent (integer division)
        exp /= 2; 
    }
    return res;
}

// Modular multiplicative inverse function: computes n^(-1) mod P
// Uses Fermat's Little Theorem: n^(P-2) % P for prime P and n non-zero mod P.
long long modInverse(long long n) {
    // We assume n is in [1, P-1] because the case q=0 is handled separately before calling this.
    return power(n, P - 2);
}

// Use std::vector<bool> which is a space-optimized specialization for boolean values.
// It will store presence information for elements {0, ..., P-1}.
std::vector<bool> isPresent; 

int main() {
    // Use fast I/O operations to speed up reading input.
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int N, Q; // N: length of sequence A, Q: number of queries
    std::cin >> N >> Q;

    std::vector<int> A(N); // Sequence A stored in a vector
    // Generate sequence A using the provided PRNG function
    for (int i = 0; i < N; ++i) {
        A[i] = xor128() % P; // Generate random value and take modulo P
    }

    // Determine a threshold for switching between two strategies.
    // A threshold of sqrt(P) is a good heuristic based on complexity analysis.
    // Using ceil ensures the threshold is at least sqrt(P).
    int threshold = static_cast<int>(std::ceil(std::sqrt(P))); 
    // For P=100003, sqrt(P) is approx 316.2, so threshold will be 317.

    if (N <= threshold) {
        // Strategy for small N: Iterate through all elements of A for each query.
        // Time complexity per query: O(N). Total time: O(N + QN).
        // Since N <= sqrt(P), total time is O(sqrt(P) + Q*sqrt(P)).
        
        for (int i = 0; i < Q; ++i) {
            int q; // Current query value
            std::cin >> q;
            
            // Handle the q=0 case separately: The maximum value is always 0.
            if (q == 0) {
                std::cout << 0 << "\n";
                continue;
            }

            int max_val = 0; // Initialize maximum value found so far
            // Problem constraints state N >= 1, so A is guaranteed to be non-empty.
            if (N > 0) { // This check is technically redundant due to N>=1 constraint but safe to have.
                // Initialize max_val with the result from the first element A[0]
                 max_val = (1LL * q * A[0]) % P; // Use 1LL to promote q to long long for multiplication
                // Iterate through the rest of the elements (from index 1)
                for (int j = 1; j < N; ++j) {
                    // Use long long for intermediate product (q * A[j]) to prevent overflow before modulo
                    long long current_val = (1LL * q * A[j]) % P;
                    // Update max_val if the current value is larger
                    if (current_val > max_val) {
                        max_val = current_val;
                    }
                }
            }
            // Output the maximum value found for this query
            std::cout << max_val << "\n";
        }
    } else {
        // Strategy for large N: Use a boolean array for fast O(1) lookups of element presence.
        // Preprocessing time: O(N) to generate A + O(P) to initialize vector<bool> + O(N) to populate it = O(N+P).
        
        isPresent.assign(P, false); // Initialize boolean array of size P to all false. O(P) time.
        // Mark elements present in A as true in the boolean array. O(N) time.
        for (int a : A) {
            isPresent[a] = true;
        }

        // Process each query using the precomputed presence information.
        // Average query time complexity: O(log P + P/|S|), where |S| is number of distinct elements in A.
        // Since N > sqrt(P), average query time is expected to be O(log P + sqrt(P)).
        // Total time: O(N+P + Q*(log P + sqrt(P))).
        
        for (int i = 0; i < Q; ++i) {
            int q; // Current query value
            std::cin >> q;

            // Handle the q=0 case separately: The maximum value is always 0.
            if (q == 0) {
                std::cout << 0 << "\n";
                continue;
            }

            // Calculate modular inverse of q needed for the alternative iteration strategy. O(log P) time.
            long long qinv = modInverse(q);
            
            // Initialize the maximum k found so far. It should default to 0 just in case.
            // Since N > threshold >= 1, A is non-empty and some k value will be found.
            int max_k = 0; 

            // Search for the largest k such that (k * qinv) % P is an element present in A.
            // Iterate k downwards from P-1 to 0.
            for (int k = P - 1; k >= 0; --k) {
                 // Calculate potential element a_k = (k * qinv) % P. 
                 // Use 64-bit intermediate type (long long) for k * qinv to prevent overflow.
                long long a_k = (static_cast<long long>(k) * qinv) % P;
                // Check if the element a_k is present in A using the boolean array (O(1) lookup).
                if (isPresent[static_cast<int>(a_k)]) {
                    max_k = k; // Found the largest k that satisfies the condition
                    break;     // Stop searching as we found the maximum possible k
                }
            }
             // Output the found maximum k value, which corresponds to the maximum (q*a)%P value.
            std::cout << max_k << "\n";
        }
    }

    return 0; // Indicate successful execution
}
0