結果

問題 No.2236 Lights Out On Simple Graph
ユーザー qwewe
提出日時 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;
      |         ~~~~~~~~~^~~~~~~

ソースコード

diff #

#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;
}
0