結果
問題 |
No.2332 Make a Sequence
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:06:50 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
WA
|
実行時間 | - |
コード長 | 10,590 bytes |
コンパイル時間 | 2,742 ms |
コンパイル使用メモリ | 128,840 KB |
実行使用メモリ | 76,696 KB |
最終ジャッジ日時 | 2025-05-14 13:08:53 |
合計ジャッジ時間 | 31,610 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 1 WA * 1 |
other | WA * 45 TLE * 1 -- * 15 |
ソースコード
#include <iostream> #include <vector> #include <string> #include <numeric> #include <algorithm> #include <map> #include <queue> #include <limits> #include <vector> // Try to include AtCoder library string header for suffix array utilities. // If building locally without AtCoder library, provide basic placeholders. #if __has_include(<atcoder/string>) #include <atcoder/string> #else // Provide placeholder implementations if AtCoder library is not available. // Warning: These placeholders might be too slow or incorrect for competitive programming. #warning "AtCoder String Library not found, using basic placeholder implementations. This might TLE or WA." namespace atcoder { // Basic O(N^2 logN) Suffix Array - Too slow for constraints std::vector<int> suffix_array(const std::vector<int>& s) { int n = s.size(); std::vector<int> sa(n); std::iota(sa.begin(), sa.end(), 0); std::sort(sa.begin(), sa.end(), [&](int i, int j) { int k = 0; while (i + k < n && j + k < n) { if (s[i + k] != s[j + k]) return s[i + k] < s[j + k]; k++; } return i + k == n; // If i suffix is prefix of j suffix, i comes first }); return sa; } // Basic O(N^2) LCP Array - Too slow std::vector<int> lcp_array(const std::vector<int>& s, const std::vector<int>& sa) { int n = s.size(); if (n == 0) return {}; std::vector<int> rank(n); for (int i = 0; i < n; ++i) rank[sa[i]] = i; std::vector<int> lcp(n); // acl returns size n, lcp[0]=0 int h = 0; for (int i = 0; i < n; ++i) { if (rank[i] > 0) { int j = sa[rank[i] - 1]; // previous suffix in sorted order // Compute LCP between suffix i and suffix j while (i + h < n && j + h < n && s[i + h] == s[j + h]) { h++; } lcp[rank[i]] = h; if (h > 0) h--; // Kasai's algorithm optimization } } return lcp; } } // namespace atcoder #endif // RMQ structure using Sparse Table for O(1) minimum queries on LCP array. template <typename T> struct SparseTable { std::vector<std::vector<T>> st; // Sparse table storage std::vector<int> log_table; // Precomputed logarithms base 2 T identity; // Identity element for the query operation (minimum -> infinity) SparseTable() {} // Default constructor // Constructor builds the sparse table from a vector v. SparseTable(const std::vector<T>& v, T id) : identity(id) { int n = v.size(); if (n == 0) return; // Handle empty vector case // Precompute logs base 2 log_table.assign(n + 1, 0); for (int i = 2; i <= n; ++i) log_table[i] = log_table[i / 2] + 1; int max_log = (n == 0) ? 0 : log_table[n]; // Max power of 2 needed st.assign(n, std::vector<T>(max_log + 1)); // Initialize base level (k=0) of sparse table for (int i = 0; i < n; ++i) st[i][0] = v[i]; // Build sparse table layers for (int k = 1; k <= max_log; ++k) { for (int i = 0; i + (1 << k) <= n; ++i) { st[i][k] = std::min(st[i][k - 1], st[i + (1 << (k - 1))][k - 1]); } } } // Query the minimum value in the range [l, r] (inclusive). T query(int l, int r) { // Check for invalid range. Use identity value if invalid. if (l > r || l < 0 || r >= (int)st.size()) { // If using outside ACL context, check size. If st empty, this might error. if (st.empty()) return identity; return identity; } // Use precomputed log to find largest power of 2 less than or equal to range length. int k = log_table[r - l + 1]; // The minimum is the minimum of two overlapping intervals covering [l, r]. return std::min(st[l][k], st[r - (1 << k) + 1][k]); } }; // Use long long for costs to prevent overflow, as total cost can be large. using CostType = long long; // Define a large value to represent infinity, carefully chosen to avoid overflow during additions. const CostType INF = std::numeric_limits<CostType>::max() / 3; int main() { // Faster I/O std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int N, M; // Lengths of sequences A and B std::cin >> N >> M; std::vector<int> A(N); // Sequence A for (int i = 0; i < N; ++i) std::cin >> A[i]; std::vector<int> B(M); // Target sequence B for (int i = 0; i < M; ++i) std::cin >> B[i]; std::vector<CostType> C(M); // Costs C_0, C_1, ..., C_{M-1} for (int i = 0; i < M; ++i) std::cin >> C[i]; // Coordinate compression: Map original values to smaller integer ranks [1..k] // This is necessary for suffix array algorithms that work on integer alphabets. std::map<int, int> val_map; std::vector<int> all_vals; all_vals.reserve(N + M); for (int x : A) all_vals.push_back(x); for (int x : B) all_vals.push_back(x); std::sort(all_vals.begin(), all_vals.end()); all_vals.erase(std::unique(all_vals.begin(), all_vals.end()), all_vals.end()); int rank_val = 1; // Start ranks from 1 for (int x : all_vals) { val_map[x] = rank_val++; } int max_char_val = rank_val; // Keep track of the largest rank used // Construct the combined sequence S = A' #1 B' #2 using compressed ranks // Use unique separator characters with values larger than any rank. std::vector<int> S_vec; S_vec.reserve(N + M + 2); for (int x : A) S_vec.push_back(val_map[x]); S_vec.push_back(max_char_val++); // Separator 1 int B_start_idx_in_S = S_vec.size(); // Store start index of B' within S for (int x : B) S_vec.push_back(val_map[x]); S_vec.push_back(max_char_val++); // Separator 2 int S_len = S_vec.size(); // Total length of combined sequence S // Compute Suffix Array (SA) and LCP Array using AtCoder Library functions // These are efficient implementations (typically O(N log N) or O(N)). std::vector<int> sa = atcoder::suffix_array(S_vec); std::vector<int> lcp = atcoder::lcp_array(S_vec, sa); // lcp[i] = LCP(SA[i], SA[i-1]) // Compute Rank array (inverse of SA): rank[i] = position of suffix S[i..] in SA std::vector<int> rank(S_len); for (int i = 0; i < S_len; ++i) rank[sa[i]] = i; // Build RMQ structure on the LCP array for O(1) LCP queries // Use S_len + 1 as identity (infinity) for min operation, as max LCP value is S_len. SparseTable<int> rmq(lcp, S_len + 1); // Lambda function to compute LCP between suffix starting at index i and suffix starting at index j in S auto get_lcp = [&](int i, int j) -> int { // Handle boundary cases if (i < 0 || i >= S_len || j < 0 || j >= S_len) return 0; if (i == j) return S_len - i; // LCP of a suffix with itself is its length // Get ranks of suffixes i and j in the suffix array int rank_i = rank[i]; int rank_j = rank[j]; // Ensure rank_i < rank_j for RMQ query range if (rank_i > rank_j) std::swap(rank_i, rank_j); // LCP(S[i..], S[j..]) = min(LCP(SA[k], SA[k-1])) for k from rank_i+1 to rank_j // Query RMQ on LCP array over the range [rank_i + 1, rank_j] return rmq.query(rank_i + 1, rank_j); }; // Dijkstra's algorithm to find the minimum cost path std::vector<CostType> dist(M + 1, INF); // dist[i] = min cost to form prefix B[0..i-1] (length i) dist[0] = 0; // Base case: cost to form empty prefix is 0 // Priority Queue stores pairs {cost, node_index}, ordered by minimum cost std::priority_queue<std::pair<CostType, int>, std::vector<std::pair<CostType, int>>, std::greater<std::pair<CostType, int>>> pq; pq.push({0, 0}); // Start Dijkstra from state 0 (empty sequence) while (!pq.empty()) { CostType d = pq.top().first; // Current minimum cost int u = pq.top().second; // Current node (length of constructed prefix) pq.pop(); // If we found a shorter path to u already, ignore this entry if (d > dist[u]) continue; // If we reached the target length M, we are done (since Dijkstra finds shortest path first) if (u == M) break; // Compute the Longest Common Prefix (LCP) between sequence A and the suffix of B starting at index u. // This determines the maximum length k such that A[0..k-1] matches B[u..u+k-1]. int l = 0; int B_suffix_start_idx = B_start_idx_in_S + u; // Index in combined string S if (B_suffix_start_idx < S_len) { // Check bounds before querying LCP l = get_lcp(0, B_suffix_start_idx); // LCP of A (starts at S[0]) and B[u..] (starts at S[B_suffix_start_idx]) } // Determine the maximum valid k for the current operation // k must be at most LCP length l, the length N of A, and the remaining length M-u needed. int K_u = std::min({l, N, M - u}); // Explore all possible operations: append A[0..k-1] for k from 1 to K_u for (int k = 1; k <= K_u; ++k) { int v = u + k; // The resulting length will be v // The cost depends on the length 'u' *before* the operation. C is 0-indexed. CostType current_C = C[u]; // Calculate the cost of this operation: k * C[u] CostType weight = (CostType)k * current_C; // Check for potential overflow before adding weights // If dist[u] + weight would exceed INF, this path is too costly. if (dist[u] >= INF - weight) { continue; } // Calculate the new total cost to reach state v via state u CostType new_dist = dist[u] + weight; // If this path is shorter than the current known shortest path to v... if (new_dist < dist[v]) { dist[v] = new_dist; // Update the minimum distance to v pq.push({dist[v], v}); // Add the updated state to the priority queue } } } // After Dijkstra finishes, check if the target state M is reachable if (dist[M] == INF) { std::cout << -1 << std::endl; // Not reachable } else { std::cout << dist[M] << std::endl; // Output the minimum cost } return 0; }