Submission #1311811

#TimeUsernameProblemLanguageResultExecution timeMemory
1311811Boycl07Hack (APIO25_hack)C++20
40.80 / 100
407 ms1884 KiB
#include <vector>
#include <numeric>
#include <algorithm>
#include <random>
#include <cmath>
#include <set>

// Declare the interactor provided by the judge
long long collisions(std::vector<long long> x);

namespace {
    // Helper to find all divisors of val
    std::vector<long long> get_divisors(long long val) {
        std::vector<long long> divs;
        for (long long i = 1; i * i <= val; ++i) {
            if (val % i == 0) {
                divs.push_back(i);
                if (val / i != i) divs.push_back(val / i);
            }
        }
        std::sort(divs.begin(), divs.end());
        return divs;
    }

    // Verify if 'n' is the hidden number (Cost: 2)
    bool check_n(long long n) {
        if (n <= 0) return false;
        std::vector<long long> q = {1, n + 1};
        return collisions(q) > 0;
    }

    // Efficiently isolate a collision from a set known to contain one
    int isolate_collision(std::vector<long long>& current_set, std::mt19937_64& rng) {
        while (true) {
            // Optimization: If set is small, brute force all pair differences
            // This saves recursion queries at the bottom of the tree
            if (current_set.size() <= 20) { 
                for (size_t i = 0; i < current_set.size(); ++i) {
                    for (size_t j = i + 1; j < current_set.size(); ++j) {
                        long long diff = std::abs(current_set[i] - current_set[j]);
                        if (diff == 0) continue;
                        
                        std::vector<long long> cands = get_divisors(diff);
                        for (long long cand : cands) {
                            // Optimization: We can skip very small candidates 
                            // if we handled them in previous phases, but checking > 1 is safe.
                            if (cand > 1 && check_n(cand)) return (int)cand;
                        }
                    }
                }
                break; // Should not happen if collision exists
            }

            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());

            // Check Left
            if (collisions(left_part) > 0) {
                current_set = left_part;
            } 
            // Check Right
            else if (collisions(right_part) > 0) {
                current_set = right_part;
            } 
            // Crossing Collision (One in Left, One in Right)
            else {
                // Shuffle to try a new split
                std::shuffle(current_set.begin(), current_set.end(), rng);
            }
        }
        return -1;
    }
}

int hack() {
    std::mt19937_64 rng(1337); 

    // --- Phase 1: Small N Binary Search Base (Linear Probe) ---
    // Efficiently handles N <= 2500
    // Cost: 2500
    {
        const int SMALL_LIMIT = 2500;
        std::vector<long long> q(SMALL_LIMIT);
        std::iota(q.begin(), q.end(), 1);
        
        if (collisions(q) > 0) {
            // Standard Binary Search for N in range [1, 2500]
            int low = 2, high = SMALL_LIMIT;
            int ans = high;
            while (low <= high) {
                int mid = low + (high - low) / 2;
                std::vector<long long> test_q(mid);
                std::iota(test_q.begin(), test_q.end(), 1);
                
                if (collisions(test_q) > 0) {
                    ans = mid;
                    high = mid - 1;
                } else {
                    low = mid + 1;
                }
            }
            return ans - 1;
        }
    }

    // --- Phase 2: Medium N (<= 1,000,000) Deterministic Check ---
    // Uses Difference Set Construction. 
    // Set Size: ~2000. Cost: 2000.
    {
        const long long B = 1000; // sqrt(1,000,000)
        std::vector<long long> diff_set;
        diff_set.reserve(2 * B);
        for (long long i = 1; i <= B; ++i) diff_set.push_back(i);
        for (long long i = 1; i <= B; ++i) diff_set.push_back(i * B);
        
        // Cleanup
        std::sort(diff_set.begin(), diff_set.end());
        diff_set.erase(std::unique(diff_set.begin(), diff_set.end()), diff_set.end());

        if (collisions(diff_set) > 0) {
            // Collision confirmed for N <= 1,000,000.
            // Isolate it using Divide & Conquer (Cost ~4000)
            return isolate_collision(diff_set, rng);
        }
    }

    // --- Phase 3: Large N (<= 10^9) Randomized Strategy ---
    // We use a small K to keep total cost low (< 110,000).
    // K = 22,000. 
    // Cost Analysis: 
    //   Phase 1 (2.5k) + Phase 2 (2k) + Phase 3 Probe (22k) + Isolation (44k) 
    //   Total ~ 70.5k. This fits SAFELY in the 110k budget.
    // If we fail the first random try, we loop. The score penalty for cost is log-based,
    // so it's better to retry than to fail.
    
    int K = 22000;
    std::uniform_int_distribution<long long> dist(1, 20000000000LL);

    while (true) {
        std::vector<long long> current_set;
        current_set.reserve(K);
        std::set<long long> distinct_check;
        
        while (current_set.size() < K) {
            long long val = dist(rng);
            if (distinct_check.insert(val).second) {
                current_set.push_back(val);
            }
        }

        if (collisions(current_set) > 0) {
            return isolate_collision(current_set, rng);
        }
        // Loop continues if no collision found (unlucky case)
    }
    
    return -1;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...