結果
問題 |
No.2236 Lights Out On Simple Graph
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:07:21 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 200 ms / 4,000 ms |
コード長 | 16,410 bytes |
コンパイル時間 | 1,333 ms |
コンパイル使用メモリ | 105,424 KB |
実行使用メモリ | 7,844 KB |
最終ジャッジ日時 | 2025-05-14 13:09:04 |
合計ジャッジ時間 | 3,520 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge5 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 57 |
コンパイルメッセージ
In file included from /usr/include/c++/13/string:51, from /usr/include/c++/13/bits/locale_classes.h:40, from /usr/include/c++/13/bits/ios_base.h:41, from /usr/include/c++/13/ios:44, from /usr/include/c++/13/ostream:40, from /usr/include/c++/13/iostream:41, from main.cpp:1: In function ‘typename __gnu_cxx::__enable_if<std::__is_scalar<_Tp>::__value, void>::__type std::__fill_a1(_ForwardIterator, _ForwardIterator, const _Tp&) [with _ForwardIterator = bool*; _Tp = bool]’, inlined from ‘void std::__fill_a(_FIte, _FIte, const _Tp&) [with _FIte = bool*; _Tp = bool]’ at /usr/include/c++/13/bits/stl_algobase.h:977:21, inlined from ‘void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&) [with _ForwardIterator = bool*; _Tp = bool]’ at /usr/include/c++/13/bits/stl_algobase.h:1007:20, inlined from ‘int main()’ at main.cpp:224:9: /usr/include/c++/13/bits/stl_algobase.h:931:18: warning: ‘void* __builtin_memset(void*, int, long unsigned int)’ specified bound between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=] 931 | *__first = __tmp; | ~~~~~~~~~^~~~~~~
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <algorithm> #include <map> // Use map for node mapping #include <vector> using namespace std; // Define unsigned long long for bit manipulation, useful for M <= 64 typedef unsigned long long ull; // Use built-in popcount if available (GCC/Clang), otherwise provide fallback #ifdef __GNUC__ #define popcount(x) __builtin_popcountll(x) #else // Fallback popcount implementation counts set bits in ull int popcount(ull n) { int count = 0; ull C = n; // Use a copy to avoid modifying input 'n' while (C > 0) { C &= (C - 1); // Clear the least significant bit set count++; } return count; } #endif // Constants for maximum N and M based on problem constraints const int MAXN_const = 40; const int MAXM_const = 40; // Global storage for graph structure vector<pair<int, int>> all_edges; // Stores all edges {u, v} as pairs of 0-indexed vertices vector<vector<int>> node_to_edge_indices; // Map node index to list of edge indices incident to it // DFS state variables bool visited[MAXN_const]; // Track visited nodes during DFS vector<int> component_nodes; // Store nodes (original 0-based indices) in the current component vector<int> component_edge_indices; // Store edge indices (original 0-based indices) in the current component // DFS function to find all nodes and edges belonging to a connected component starting from node u void dfs_component(int u) { visited[u] = true; component_nodes.push_back(u); // Iterate through edges incident to node u for (int edge_idx : node_to_edge_indices[u]) { // Find the other endpoint v of the edge int v = all_edges[edge_idx].first == u ? all_edges[edge_idx].second : all_edges[edge_idx].first; // Check if this edge has already been added to the component's edge list bool edge_found = false; for(int existing_edge_idx : component_edge_indices) { if (edge_idx == existing_edge_idx) { edge_found = true; break; } } // If not found, add it if (!edge_found) { component_edge_indices.push_back(edge_idx); } // If neighbor v hasn't been visited, recurse if (!visited[v]) { dfs_component(v); } } } /** * Performs Gaussian elimination over F2 (modulo 2) on the system Ax = C. * A is represented as a vector of ull, where each ull represents a row with bits corresponding to columns. * C is the right-hand side vector. * N is the number of rows (vertices in component), M is the number of columns (edges in component). * Finds a basis for the null space (solutions to Ax = 0) and stores it in `basis`. * Stores the original column index of the pivot variable for each row in `pivot_row_original_col`. * Returns the rank of the matrix A. Returns -1 if the system is inconsistent (Ax=C has no solution). */ int gaussian_elimination(vector<ull>& A, vector<int>& C, int N, int M, vector<ull>& basis, vector<int>& pivot_row_original_col) { basis.clear(); // Clear previous basis vectors pivot_row_original_col.assign(N, -1); // Initialize pivot column info for each row int rank = 0; // Current rank vector<bool> is_pivot_col(M, false); // Track which columns contain a pivot // Forward elimination phase for (int current_col = 0; current_col < M && rank < N; ++current_col) { // Find a row >= rank with a 1 in the current column int pivot_row = rank; while (pivot_row < N && !(A[pivot_row] & (1ULL << current_col))) { pivot_row++; } // If no pivot found in this column, continue to the next column if (pivot_row == N) continue; // Swap the found pivot row with the current rank row swap(A[rank], A[pivot_row]); swap(C[rank], C[pivot_row]); // Record the pivot information pivot_row_original_col[rank] = current_col; is_pivot_col[current_col] = true; // Mark this column as containing a pivot // Eliminate 1s in the current column in all other rows for (int i = 0; i < N; ++i) { if (i != rank && (A[i] & (1ULL << current_col))) { A[i] ^= A[rank]; // Row i = Row i + Row rank (mod 2) C[i] ^= C[rank]; // C[i] = C[i] + C[rank] (mod 2) } } rank++; // Increment rank } // Check for inconsistency: If any row after rank has C[i] = 1, system is inconsistent for (int i = rank; i < N; ++i) { if (C[i] == 1) { return -1; } } // Find basis for the null space Ax = 0 for (int j = 0; j < M; ++j) { // If column j is not a pivot column, it corresponds to a free variable if (!is_pivot_col[j]) { ull b = (1ULL << j); // Create a basis vector starting with 1 at the free variable position j // Determine the values of pivot variables for this basis vector for (int r = 0; r < rank; ++r) { int p_col = pivot_row_original_col[r]; // Pivot column for row r // If the equation for row r (A[r]) involves the free variable x_j if (A[r] & (1ULL << j)) { // Set the corresponding pivot variable x_{p_col} to 1 to satisfy Ax = 0 b |= (1ULL << p_col); } } basis.push_back(b); // Add the completed basis vector } } return rank; // Return the rank of the matrix } /** * Finds a particular solution xp to the system Ax = C (after Gaussian elimination). * Uses back substitution, setting all free variables to 0. * A and C are the matrix and vector after Gaussian elimination. * N, M, rank are dimensions and rank. * pivot_row_original_col maps row index to the column index of its pivot. */ ull find_particular_solution(const vector<ull>& A, const vector<int>& C, int N, int M, int rank, const vector<int>& pivot_row_original_col) { ull xp = 0; // Initialize particular solution vector to all zeros // Back substitution: iterate through rows from bottom (rank-1) up to 0 for (int r = rank - 1; r >= 0; --r) { int p_col = pivot_row_original_col[r]; // The pivot variable column index for this row r // If p_col is -1, it means row r is a zero row (should not happen for r < rank) // Or there was an issue. For safety, skip if p_col invalid. if (p_col == -1) { continue; } int val = C[r]; // Target value for the equation represented by row r // Calculate the contribution of already determined pivot variables (columns > p_col) for (int c = p_col + 1; c < M; ++c) { // Check if the equation for row r involves variable x_c if (A[r] & (1ULL << c)) { // Check if x_c is a pivot variable (not a free variable) bool c_is_pivot = false; for(int rr=0; rr < rank; ++rr){ // Check if column c is a pivot column for any row if(pivot_row_original_col[rr] == c){ c_is_pivot = true; break; } } // If x_c is a pivot variable AND its value in xp (already determined) is 1 if(c_is_pivot && (xp & (1ULL << c))) { val ^= 1; // Adjust the target value `val` based on x_c's contribution (mod 2) } // Note: Free variables are set to 0, so their contribution is always 0. No need to check them. } } // After accounting for contributions from variables c > p_col, `val` is the required value for x_{p_col}. if (val) { // If required value for the pivot variable x_{p_col} is 1 xp |= (1ULL << p_col); // Set the corresponding bit in xp } } return xp; // Return the computed particular solution } int main() { // Faster I/O ios_base::sync_with_stdio(false); cin.tie(NULL); int N, M; // Number of vertices and edges cin >> N >> M; // Read edges and build adjacency list representation for edges incident to each node all_edges.resize(M); node_to_edge_indices.assign(N, vector<int>()); for (int i = 0; i < M; ++i) { int u, v; cin >> u >> v; --u; --v; // Use 0-based indexing all_edges[i] = {u, v}; node_to_edge_indices[u].push_back(i); node_to_edge_indices[v].push_back(i); } // Read initial colors of vertices vector<int> initial_colors(N); for (int i = 0; i < N; ++i) { cin >> initial_colors[i]; } long long total_min_ops = 0; // Accumulator for the total minimum operations fill(visited, visited + N, false); // Initialize visited array for DFS // Iterate through all vertices to find connected components for (int i = 0; i < N; ++i) { if (!visited[i]) { // Found a start of a new component (or an isolated node) component_nodes.clear(); // Reset component node list component_edge_indices.clear(); // Reset component edge list // Handle isolated nodes: if a node has no edges connected to it if (node_to_edge_indices[i].empty()) { visited[i] = true; // Mark as visited // If an isolated node is black, it's impossible to make it white if (initial_colors[i] == 1) { cout << -1 << endl; return 0; } // Isolated white node requires 0 operations, continue to next potential component continue; } // Perform DFS to find all nodes and edges in the current connected component dfs_component(i); // Check the parity condition: number of black nodes in the component must be even int current_comp_black_nodes = 0; for (int node_idx : component_nodes) { if (initial_colors[node_idx] == 1) { current_comp_black_nodes++; } } // If odd, impossible to solve if (current_comp_black_nodes % 2 != 0) { cout << -1 << endl; return 0; } // If the component has nodes but no edges (should not happen if dfs starts from node with edges, but safety check) if (component_edge_indices.empty()) { // This implies all nodes in the component are isolated from each other within this component context. // If any are black, impossible. if (current_comp_black_nodes > 0) { cout << -1 << endl; return 0; } // All nodes are white, requires 0 operations. continue; } // Component size int N_comp = component_nodes.size(); int M_comp = component_edge_indices.size(); // Create mappings from global node/edge indices to local 0..N_comp-1 / 0..M_comp-1 indices map<int, int> node_map; for(int j=0; j<N_comp; ++j) node_map[component_nodes[j]] = j; map<int, int> edge_map; for(int j=0; j<M_comp; ++j) edge_map[component_edge_indices[j]] = j; // Build the incidence matrix A and target vector C for this component's linear system vector<ull> A_comp(N_comp, 0ULL); // Matrix rows (one per node in component) vector<int> C_comp(N_comp); // Target color vector (1 if black, 0 if white) for(int j=0; j<N_comp; ++j) C_comp[j] = initial_colors[component_nodes[j]]; // Set target colors // Populate the matrix A for(int j=0; j<M_comp; ++j) { int global_edge_idx = component_edge_indices[j]; // Original edge index int u_global = all_edges[global_edge_idx].first; // Endpoint 1 (global index) int v_global = all_edges[global_edge_idx].second; // Endpoint 2 (global index) int edge_local_idx = j; // Local index for this edge (column index) // Set bits in A matrix corresponding to the endpoints of the edge if (node_map.count(u_global)) { // Check if u is in this component int u_local = node_map[u_global]; // Get local index for u A_comp[u_local] |= (1ULL << edge_local_idx); // Set bit for edge j in row u_local } if (node_map.count(v_global)) { // Check if v is in this component int v_local = node_map[v_global]; // Get local index for v A_comp[v_local] |= (1ULL << edge_local_idx); // Set bit for edge j in row v_local } } // Solve the linear system Ax = C for this component vector<ull> basis; // Basis vectors for the null space vector<ull> A_copy = A_comp; // Copy A as GE modifies it vector<int> C_copy = C_comp; // Copy C as GE modifies it vector<int> pivot_row_original_col; // Stores pivot column indices found by GE int rank = gaussian_elimination(A_copy, C_copy, N_comp, M_comp, basis, pivot_row_original_col); // If system is inconsistent (should have been caught by parity check) if (rank == -1) { cout << -1 << endl; return 0; } // Find one particular solution xp to Ax = C ull xp = find_particular_solution(A_copy, C_copy, N_comp, M_comp, rank, pivot_row_original_col); int k = basis.size(); // Number of free variables = dimension of null space if (k == 0) { // If k=0, the solution xp is unique total_min_ops += popcount(xp); // Add its weight (number of operations) } else { // If k > 0, there are multiple solutions. Find the minimum weight one. int min_comp_weight = MAXM_const + 1; // Initialize minimum weight to infinity // Meet-in-the-middle approach: split basis into two halves int k1 = k / 2; int k2 = k - k1; // Generate vectors y = xp + sum(alpha_i * b_i) for first half of basis vectors vector<ull> listY; listY.reserve(1 << k1); for(int mask = 0; mask < (1 << k1); ++mask) { ull current_y = xp; for(int bit = 0; bit < k1; ++bit) { if((mask >> bit) & 1) { // If bit is set, XOR with corresponding basis vector current_y ^= basis[bit]; } } listY.push_back(current_y); } // Generate vectors z = sum(alpha_j * b_j) for second half of basis vectors vector<ull> listZ; listZ.reserve(1 << k2); for(int mask = 0; mask < (1 << k2); ++mask) { ull current_z = 0; // Start from zero vector for(int bit = 0; bit < k2; ++bit) { if((mask >> bit) & 1) { // If bit is set, XOR with corresponding basis vector (indices k1 to k-1) current_z ^= basis[k1 + bit]; } } listZ.push_back(current_z); } // Find the minimum Hamming weight of y XOR z over all pairs (y from listY, z from listZ) // This minimum weight corresponds to the minimum number of operations for this component. min_comp_weight = MAXM_const + 1; for (ull y : listY) { for (ull z : listZ) { min_comp_weight = min(min_comp_weight, popcount(y ^ z)); } } total_min_ops += min_comp_weight; // Add minimum weight for this component to total } } } // Output the total minimum number of operations cout << total_min_ops << endl; return 0; }