結果
問題 |
No.1917 LCMST
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:12:46 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 379 ms / 4,000 ms |
コード長 | 7,902 bytes |
コンパイル時間 | 1,147 ms |
コンパイル使用メモリ | 91,296 KB |
実行使用メモリ | 48,956 KB |
最終ジャッジ日時 | 2025-05-14 13:14:02 |
合計ジャッジ時間 | 15,901 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge5 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 42 |
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <algorithm> #include <tuple> #include <limits> // Needed for numeric_limits // Define long long type for potentially large costs typedef long long ll; // Define the maximum possible value for A_i based on problem constraints const int MAX_VAL = 100005; // Global arrays to store counts and presence of A_i values int count[MAX_VAL]; // Stores count of each A_i value bool present[MAX_VAL]; // Tracks which A_i values are present in the input std::vector<int> distinct_values; // Stores the distinct A_i values present in the input // Disjoint Set Union (DSU) structure struct DSU { std::vector<int> parent; // Constructor initializes DSU for elements indexed 1 to n. Array size n+1. DSU(int n) { parent.resize(n + 1); // Initialize each element as its own parent std::iota(parent.begin(), parent.end(), 0); } // Find the representative (root) of the set containing x, with path compression optimization int find(int x) { if (parent[x] == x) { return x; } // Path compression: set parent directly to the root return parent[x] = find(parent[x]); } // Unite the sets containing x and y void unite(int x, int y) { int rootX = find(x); int rootY = find(y); // If they are not already in the same set, merge them if (rootX != rootY) { // Simple merge: make rootX child of rootY. Could use union by rank/size for further optimization. parent[rootX] = rootY; } } }; // Computes greatest common divisor (GCD) using Euclidean algorithm ll gcd(ll a, ll b) { while (b) { a %= b; std::swap(a, b); } return a; } // Computes least common multiple (LCM) using the formula lcm(a, b) = (a / gcd(a, b)) * b // This implementation assumes the final LCM result fits within a 64-bit long long, // based on problem constraints (max A_i = 10^5, max LCM approx 10^10). ll lcm(ll a, ll b) { // Handle edge case where a or b is 0 if (a == 0 || b == 0) return 0; // Compute GCD first ll common_divisor = gcd(a, b); // If GCD is 0, should not happen for positive a, b if (common_divisor == 0) return 0; // Calculate (a / gcd(a, b)) first. This reduces the magnitude before multiplication, // helping to prevent intermediate overflow. ll term1 = a / common_divisor; // Perform the final multiplication. Potential overflow check using 128-bit integers // is possible with GCC/Clang (#ifdef __GNUC__ ... unsigned __int128 ...) but omitted // here for simplicity and assuming constraints guarantee result fits in long long. return term1 * b; } // multiples[g] will store a list of distinct values k present in the input A array such that g divides k std::vector<int> multiples[MAX_VAL]; int main() { // Use fast I/O operations std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int N; // Number of vertices std::cin >> N; int max_A = 0; // Track the maximum A_i value encountered // Read input values A_i for (int i = 0; i < N; ++i) { int A_i; std::cin >> A_i; // Record presence and count of A_i if (!present[A_i]) { // If seeing this value for the first time present[A_i] = true; distinct_values.push_back(A_i); // Add to list of distinct values } count[A_i]++; // Increment count for this value // Update maximum A_i value seen so far if (A_i > max_A) { max_A = A_i; } } ll total_cost = 0; // Initialize total MST cost // Step 1: Calculate initial MST cost within groups of vertices having the same A_i value. // For each distinct value k present in the input: // All count[k] vertices with value k form a clique where edges have weight lcm(k, k) = k. // To connect these count[k] vertices into one component, we need count[k]-1 edges of weight k. for (int k : distinct_values) { total_cost += (ll)(count[k] - 1) * k; } // Precompute the list of multiples for each potential GCD value g up to max_A. // This helps efficiently find pairs (k, l) related by a common divisor g later. for (int g = 1; g <= max_A; ++g) { // Iterate through all multiples k of g up to max_A for (int k = g; k <= max_A; k += g) { // If k is one of the values present in the input A array if (present[k]) { // Add k to the list of multiples for g multiples[g].push_back(k); } } } // Stores potential edges for the MST calculation between components of distinct values. // Each tuple is (weight, u, v) where weight = lcm(u, v). std::vector<std::tuple<ll, int, int>> potential_edges; // Step 2: Generate candidate edges connecting components based on GCD property. // This approach is derived from a technique used in similar competitive programming problems. // It generates a set of edges guaranteed to contain the MST edges connecting the components. // Iterate through all possible GCD values g from 1 up to max_A for (int g = 1; g <= max_A; ++g) { // If there are fewer than 2 multiples of g present among the distinct A_i values, // we cannot form an edge based on this GCD g. if (multiples[g].size() < 2) continue; // Find the minimum value k_min among the multiples of g present in the input. // This node k_min acts as a hub for connecting other multiples of g. int min_k = multiples[g][0]; for(size_t i = 1; i < multiples[g].size(); ++i){ if(multiples[g][i] < min_k) min_k = multiples[g][i]; } // Add potential edges connecting k_min to all other multiples k of g. // The edge weight is lcm(k, k_min). for (int k : multiples[g]) { if (k != min_k) { // Don't add edge from min_k to itself potential_edges.emplace_back(lcm((ll)k, (ll)min_k), k, min_k); } } } // Sort the generated potential edges by their weight (LCM value) in non-decreasing order. std::sort(potential_edges.begin(), potential_edges.end()); // Step 3: Run Kruskal's algorithm on the components representing distinct values. // Use DSU structure initialized for values up to max_A. DSU dsu(max_A); int num_distinct_values = distinct_values.size(); int edges_count = 0; // Count number of edges added to connect the components // If there's only 0 or 1 distinct value type, the graph is already connected (or empty). // The initial cost calculated in Step 1 is the final answer. if (num_distinct_values > 1) { // Iterate through sorted potential edges for (const auto& edge : potential_edges) { ll weight; int u, v; std::tie(weight, u, v) = edge; // Unpack edge tuple // Check if the components containing u and v are already connected if (dsu.find(u) != dsu.find(v)) { // If not connected, add this edge to the MST dsu.unite(u, v); // Merge the components total_cost += weight; // Add the edge weight to the total MST cost edges_count++; // Increment the count of edges added // We need exactly num_distinct_values - 1 edges to connect all components. // If we have added enough edges, stop Kruskal's algorithm. if (edges_count == num_distinct_values - 1) { break; } } } } // Output the final total MST cost std::cout << total_cost << std::endl; return 0; }