結果
問題 | No.308 素数は通れません |
ユーザー |
![]() |
提出日時 | 2025-05-14 13:25:52 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 8,222 bytes |
コンパイル時間 | 1,141 ms |
コンパイル使用メモリ | 93,448 KB |
実行使用メモリ | 20,512 KB |
最終ジャッジ日時 | 2025-05-14 13:27:15 |
合計ジャッジ時間 | 5,776 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 58 TLE * 1 -- * 48 |
ソースコード
#include <iostream> #include <vector> #include <string> #include <queue> #include <set> // Using set for visited. Unordered_set could be faster if hashing is efficient. #include <algorithm> #include <vector> // For Miller-Rabin bases // For N up to 10^24, need 64-bit integers or larger. // Using long long, assuming N fits or test cases are within its limits for path values. using int64 = long long; // Miller-Rabin Primality Test // Computes (base^exp) % mod int64 power(int64 base, int64 exp, int64 mod) { int64 res = 1; base %= mod; while (exp > 0) { if (exp % 2 == 1) res = (__int128)res * base % mod; base = (__int128)base * base % mod; exp /= 2; } return res; } // Miller-Rabin test for one base a // n is the number to test, a is the base, d and s are such that n-1 = d * 2^s bool check_composite(int64 n, int64 a, int64 d, int s) { int64 x = power(a, d, n); if (x == 1 || x == n - 1) return false; // n might be prime (witness didn't prove composite) for (int r = 1; r < s; r++) { x = (__int128)x * x % n; // Square x if (x == n - 1) return false; // n might be prime } return true; // n is definitely composite }; // Primality test using Miller-Rabin bool is_prime(int64 n) { if (n < 2) return false; // Small primes if (n == 2 || n == 3 || n == 5 || n == 7) return true; if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0) return false; // Find d and s for n-1 = d * 2^s int s = 0; int64 d = n - 1; while ((d & 1) == 0) { // while d is even d >>= 1; // d = d / 2 s++; } // Bases for deterministic Miller-Rabin for n < 2^64 (~1.8e19) // Using a common set for competitive programming. For true 10^24, more bases/probabilistic needed. std::vector<int64> bases = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; // Note: If N can be truly 10^24 (larger than 2^64), these bases are not sufficient for deterministic test. // But typically problems imply N or relevant numbers fit standard types / tests. for (int64 a : bases) { if (n == a) return true; // n is one of the small prime bases if (check_composite(n, a, d, s)) return true; // Composite (check_composite returns true if composite) // So is_prime should return false } return false; // Probably prime (is_prime should return true) } // Corrected logic for is_prime return based on check_composite bool is_prime_corrected(int64 n) { if (n < 2) return false; if (n == 2 || n == 3 || n == 5 || n == 7 ) return true; // Added 5, 7 for consistency if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0) return false; // Added %5, %7 int s = 0; int64 d = n - 1; while ((d & 1) == 0) { d >>= 1; s++; } std::vector<int64> bases = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; // For n < 10^18 according to some sources. Other sources use {2,3,5,7,11,13,17,19,23,29,31,37} for n < 2^64. Using the shorter list first. // The set {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37} is for n < 3.3 * 10^25 IF n is NOT long long. If n is long long (max ~9*10^18), a smaller set like {2,3,5,7,11,13,17} or {2,7,61} for n < 2^32 can be adapted. // Let's use the 12 primes for <2^64: {2,3,5,7,11,13,17,19,23,29,31,37}. std::vector<int64> full_bases = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; for (int64 a : full_bases) { if (n == a) return true; // If n is one of the bases, it's prime. // (already covered by small prime checks if a is small) if (a >= n) break; // Optimization: only test bases smaller than n if (check_composite(n, a, d, s)) { return false; // Composite found by witness a } } return true; // No witness found, n is (probably/deterministically) prime } // Wrapper: can pass through cell `val` if it's not prime. 1 is not prime. bool can_pass(int64 val) { if (val == 1) return true; return !is_prime_corrected(val); } int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int64 N; std::cin >> N; int64 min_W_overall = N - 1; // W=N-1 is always a valid solution (path 1 -> N). // Iterate W up to a certain limit K_W_MAX. // If N is small, this limit is N-1 itself. int K_W_MAX = 400; // Heuristic limit for W to check // Max W to check is min(N-1, K_W_MAX), but at least 1. int64 W_check_limit = std::min(N - 1, (int64)K_W_MAX); if (W_check_limit == 0 && N > 1) W_check_limit = 1; // e.g. if N=1, loop won't run. N >= 4 so N-1 >= 3. for (int64 W = 1; W <= W_check_limit; ++W) { if (W >= min_W_overall) { // Optimization: if current W is already >= best found, stop. break; } // Optimization: if 1 is trapped // Cell 1's neighbors are 2 (right, if W != 1) and 1+W (down). // Cell 2 is prime. So if 1+W is also prime (and 1+W <= N), then 1 is stuck. // This holds if W < N-1. (If W=N-1, 1+W=N, N is NP, so can move to N) if (W < N - 1 && (1 + W <= N)) { // Check if 1+W is a valid cell if (!can_pass(1+W)) { // If 1+W is prime // Cell 2 is prime (can_pass(2) is false) // So if W != 1 (meaning 2 is a distinct neighbor from 1+W), 1 is stuck // If W == 1, 1+W == 2. We just checked 1+W. So if 2 is prime, 1 is stuck. continue; } } std::queue<int64> q; std::set<int64> visited; if (!can_pass(1)) continue; // Should not happen as 1 is not prime. q.push(1); visited.insert(1); bool found_N_for_this_W = false; int states_processed = 0; const int MAX_BFS_STATES = 200000; // Limit BFS states while(!q.empty() && states_processed < MAX_BFS_STATES) { int64 curr = q.front(); q.pop(); states_processed++; if (curr == N) { found_N_for_this_W = true; break; } std::vector<int64> potential_neighbors; int64 r_curr = (curr - 1) / W + 1; int64 c_curr = (curr - 1) % W + 1; int64 H_main = N / W; int64 W_rem = N % W; // Up if (r_curr == H_main + 1 && W_rem != 0) { // curr is in remainder block potential_neighbors.push_back(curr - W); } else if (r_curr > 1 && r_curr <= H_main) { // curr is in main block, not first row potential_neighbors.push_back(curr - W); } // Down if (r_curr < H_main) { // curr is in main block, not last row of main block potential_neighbors.push_back(curr + W); } else if (r_curr == H_main && W_rem != 0 && c_curr <= W_rem) { // curr is in last row of main block, above a cell in remainder block potential_neighbors.push_back(curr + W); } // Left if (c_curr > 1) { potential_neighbors.push_back(curr - 1); } // Right int64 current_row_width = (r_curr <= H_main) ? W : W_rem; if (r_curr == H_main + 1 && W_rem == 0) current_row_width = 0; // Should not happen: in remainder implies W_rem != 0 if (c_curr < current_row_width) { potential_neighbors.push_back(curr + 1); } for (int64 neighbor_val : potential_neighbors) { if (neighbor_val >= 1 && neighbor_val <= N) { // Check bounds if (visited.find(neighbor_val) == visited.end()) { if (can_pass(neighbor_val)) { visited.insert(neighbor_val); q.push(neighbor_val); } } } } } if (found_N_for_this_W) { min_W_overall = std::min(min_W_overall, W); } } std::cout << min_W_overall << std::endl; return 0; }