Submission #1310302

#TimeUsernameProblemLanguageResultExecution timeMemory
1310302Boycl07Hack (APIO25_hack)C++20
39.50 / 100
283 ms1428 KiB
#include "hack.h"
#include <vector>
#include <numeric>
#include <algorithm>
#include <random>
#include <cmath>

// Declare the interactor provided by the judge

namespace {
    // Helper to find all divisors of val that are strictly greater than limit.
    // Since we constrain input values to ~10^10, val is small enough to factorize quickly.
    std::vector<long long> get_divisors(long long val, long long limit) {
        std::vector<long long> divs;
        for (long long i = 1; i * i <= val; ++i) {
            if (val % i == 0) {
                if (i > limit) divs.push_back(i);
                if (val / i > limit && val / i != i) divs.push_back(val / i);
            }
        }
        std::sort(divs.begin(), divs.end());
        return divs;
    }

    // Check if a candidate n is the correct answer
    // If n is correct, 1 and n+1 will collide (both map to bucket 1)
    bool check_n(long long n) {
        if (n <= 0) return false;
        std::vector<long long> q = {1, n + 1};
        return collisions(q) > 0;
    }
}

int hack() {
    // Phase 1: Check for small n efficiently
    // If n <= 2500, a set of size 2501 {1..2501} guarantees a collision.
    const int SMALL_LIMIT = 2500;
    std::vector<long long> small_q(SMALL_LIMIT + 1);
    std::iota(small_q.begin(), small_q.end(), 1);
    
    if (collisions(small_q) > 0) {
        // Binary search to find the smallest k such that {1..k} has a collision.
        // The first collision occurs when k = n + 1.
        int low = 2, high = SMALL_LIMIT + 1;
        int ans = high;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            std::vector<long long> q(mid);
            std::iota(q.begin(), q.end(), 1);
            if (collisions(q) > 0) {
                ans = mid;
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return ans - 1;
    }

    // Phase 2: Solve for large n using Birthday Paradox
    // Use a fixed seed for consistency, or random_device if allowed.
    std::mt19937_64 rng(1337); 
    
    // CRITICAL OPTIMIZATION:
    // We restrict the random range to [1, 2*10^10].
    // This is > 10^9 (so collisions occur naturally based on n)
    // but small enough that |u - v| can be factorized instantly without TLE.
    std::uniform_int_distribution<long long> dist(1, 20000000000LL);

    // K = 50,000 fits the budget (approx 100k-110k total cost) and gives high collision prob.
    int K = 50000;
    
    // We loop until success. Even if one attempt fails, the retry will likely succeed.
    while (true) {
        std::vector<long long> current_set;
        current_set.reserve(K);
        
        // Generate distinct random numbers
        while (current_set.size() < K) {
            current_set.push_back(dist(rng));
        }
        std::sort(current_set.begin(), current_set.end());
        current_set.erase(std::unique(current_set.begin(), current_set.end()), current_set.end());
        // Refill if duplicates were removed (extremely rare)
        while (current_set.size() < K) {
            current_set.push_back(dist(rng));
            std::sort(current_set.begin(), current_set.end());
            current_set.erase(std::unique(current_set.begin(), current_set.end()), current_set.end());
        }

        if (collisions(current_set) > 0) {
            // Collision found. Use Randomized Divide & Conquer to find the pair.
            while (current_set.size() > 2) {
                int sz = current_set.size();
                int mid = sz / 2;
                std::vector<long long> left_part(current_set.begin(), current_set.begin() + mid);
                std::vector<long long> right_part(current_set.begin() + mid, current_set.end());
                
                if (collisions(left_part) > 0) {
                    current_set = left_part;
                } else if (collisions(right_part) > 0) {
                    current_set = right_part;
                } else {
                    // Collision is between Left and Right. Shuffle and retry split.
                    std::shuffle(current_set.begin(), current_set.end(), rng);
                }
            }
            
            // Calculate difference D = |u - v|. We know n | D.
            long long diff = std::abs(current_set[0] - current_set[1]);
            
            // Check divisors of D to find n.
            std::vector<long long> cands = get_divisors(diff, SMALL_LIMIT);
            for (long long cand : cands) {
                if (check_n(cand)) {
                    return (int)cand;
                }
            }
        }
        // If no collision in this random set, loop continues and tries a new set.
        // This prevents WA by ensuring we eventually find n.
    }
    
    return -1; // Should not be reached
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...