結果

問題 No.470 Inverse S+T Problem
ユーザー qwewe
提出日時 2025-05-14 12:57:56
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 103 ms / 2,000 ms
コード長 12,646 bytes
コンパイル時間 3,116 ms
コンパイル使用メモリ 122,332 KB
実行使用メモリ 36,748 KB
最終ジャッジ日時 2025-05-14 12:59:23
合計ジャッジ時間 3,876 ms
ジャッジサーバーID
(参考情報)
judge5 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 4
other AC * 27
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <vector>
#include <tuple>
#include <algorithm>
#include <set> // Include set if needed, though current logic doesn't use it directly

using namespace std;

// Structure to represent the 2-SAT problem using Kosaraju's algorithm for SCCs
struct TwoSAT {
    int N_orig; // Original number of variables (N)
    int n_vars; // Current total number of variables (N + auxiliary)
    vector<vector<int>> adj; // Adjacency list for implication graph
    vector<vector<int>> rev_adj; // Adjacency list for reversed graph
    vector<bool> visited; // Visited flags for DFS traversals
    vector<int> component; // Stores SCC component ID for each literal
    vector<int> order; // Stores finish order from first DFS
    int current_comp; // Counter for SCC IDs

    // Constructor initializes with N original variables
    TwoSAT(int N) : N_orig(N), n_vars(N) {
        adj.resize(2 * N); 
        rev_adj.resize(2 * N);
    }

    // Resizes the graph structures if more variables (auxiliary) are added
    void resize(int new_n_vars) {
        if (new_n_vars > n_vars) {
             adj.resize(2 * new_n_vars);
             rev_adj.resize(2 * new_n_vars);
             n_vars = new_n_vars;
        }
    }
    
    // Computes the negation of a literal index
    // Variable i is represented by literals 2*i (positive) and 2*i+1 (negative)
    int neg(int lit) { return lit ^ 1; }

    // Adds a directed edge u -> v to the implication graph
    void add_edge(int u, int v) {
        adj[u].push_back(v);
        rev_adj[v].push_back(u);
    }

    // Adds a clause (u OR v) to the 2-SAT instance.
    // This is equivalent to two implications: (NOT u -> v) and (NOT v -> u)
    void add_clause(int u, int v) {
        // Basic boundary check, important if resize logic has potential issues
        if (neg(u) >= adj.size() || v >= adj.size() || neg(v) >= adj.size() || u >= adj.size()) {
           cerr << "Error: Literal index out of bounds during clause addition." << endl;
           exit(1); // Critical error, exit
        }
        add_edge(neg(u), v);
        add_edge(neg(v), u);
    }

    // First DFS pass for Kosaraju's algorithm: compute finish times (order)
    void dfs1(int u) {
        visited[u] = true;
        for (int v : adj[u]) {
            if (!visited[v]) {
                dfs1(v);
            }
        }
        order.push_back(u);
    }

    // Second DFS pass for Kosaraju's algorithm: find SCCs on the reversed graph
    void dfs2(int u) {
        visited[u] = true;
        component[u] = current_comp;
        for (int v : rev_adj[u]) {
            if (!visited[v]) {
                dfs2(v);
            }
        }
    }

    // Solves the 2-SAT problem. Returns true if satisfiable, false otherwise.
    // Fills the `assignment` vector with truth values for the original N variables if satisfiable.
    bool solve(vector<bool>& assignment) {
        int num_nodes = 2 * n_vars; // Total literals = 2 * total variables
        visited.assign(num_nodes, false);
        order.clear();
        order.reserve(num_nodes); // Reserve space for efficiency
        // Perform first DFS pass on all nodes to compute finish order
        for (int i = 0; i < num_nodes; ++i) {
            if (!visited[i]) {
                dfs1(i);
            }
        }

        visited.assign(num_nodes, false);
        component.assign(num_nodes, -1); // Initialize component IDs to -1
        current_comp = 0; // Start component IDs from 0
        reverse(order.begin(), order.end()); // Process nodes in reverse finish order
        // Perform second DFS pass on reversed graph to identify SCCs
        for (int u : order) {
            if (!visited[u]) { 
                dfs2(u);
                current_comp++; // Increment component ID for next SCC
            }
        }

        // Check for contradictions: variable `i` and its negation `NOT i` in the same SCC
        for (int i = 0; i < N_orig; ++i) { 
            if (component[2 * i] == component[2 * i + 1]) {
                 // A conflict exists only if both literals belong to a valid SCC (component ID != -1)
                 if (component[2 * i] != -1) return false; // Unsatisfiable
            }
         }
        
        // If no conflicts, find a satisfying assignment for original N variables
        assignment.resize(N_orig); 
        for (int i = 0; i < N_orig; ++i) {
             // Assign true to variable `i` if its positive literal `2*i` is in a component with a higher ID 
             // than its negative literal `2*i+1`. This corresponds to topological order of SCCs.
             assignment[i] = (component[2 * i] > component[2 * i + 1]);
        }

        return true; // Satisfiable
    }
};


// Map to store occurrences of each potential string: string -> list of (original_index, option_index, type)
map<string, vector<tuple<int, int, char>>> occurrences;
// Store the two possible pairs (S, T) for each input U_i
vector<pair<string, string>> options[100005]; 

int main() {
    ios_base::sync_with_stdio(false); // Faster I/O
    cin.tie(NULL);

    int N; // Number of input strings U_i
    cin >> N;

    // Process each input string U_i
    for (int i = 0; i < N; ++i) {
        string U;
        cin >> U;
        options[i].reserve(2); // Optimization: reserve space for 2 pairs

        // Option 0: lengths (1, 2)
        string s1 = U.substr(0, 1);
        string t1 = U.substr(1, 2);
        options[i].push_back({s1, t1});
        // Record occurrences of s1 and t1 for this option
        occurrences[s1].emplace_back(i, 0, 'S'); 
        occurrences[t1].emplace_back(i, 0, 'T');

        // Option 1: lengths (2, 1)
        string s2 = U.substr(0, 2);
        string t2 = U.substr(2, 1);
        options[i].push_back({s2, t2});
        // Record occurrences of s2 and t2 for this option
        occurrences[s2].emplace_back(i, 1, 'S'); 
        occurrences[t2].emplace_back(i, 1, 'T');
    }

    // Initialize 2-SAT solver with N variables
    TwoSAT ts(N);
    int current_vars_count = N; // Track total variables including auxiliary ones

    // Iterate through all unique strings found and their occurrences
    for (auto const& [str, occ_list] : occurrences) {
        vector<int> always_indices; // Indices i for which string 'str' appears regardless of option chosen
        vector<tuple<int, int, char>> conditional_occurrences; // Occurrences where 'str' appears only for a specific option
        map<int, vector<pair<int, char>>> occurrences_by_index; // Group occurrences by original index i

        // Group occurrences by index i
        for (const auto& occ : occ_list) {
            // get<0>(occ) = index i (0..N-1), get<1>(occ) = option k (0 or 1)
            occurrences_by_index[get<0>(occ)].push_back({get<1>(occ), get<2>(occ)});
        }

        // Identify always-X indices and conditional occurrences
        for (auto const& [idx, pairs] : occurrences_by_index) {
             bool has_opt0 = false, has_opt1 = false;
             for(const auto& p : pairs) { // Check if string 'str' occurs for both option 0 and option 1 for index idx
                 if (p.first == 0) has_opt0 = true;
                 if (p.first == 1) has_opt1 = true;
             }

             if (has_opt0 && has_opt1) { // If occurs for both options, index idx is an always-X index
                  always_indices.push_back(idx);
             } else { // Otherwise, all occurrences for this index are conditional
                  for(const auto& p : pairs) {
                       conditional_occurrences.emplace_back(idx, p.first, p.second);
                  }
             }
        }
        
        // Check for immediate impossibility: If string 'str' must appear for two different indices regardless of choice
        if (always_indices.size() >= 2) {
            cout << "Impossible" << endl;
            return 0;
        }

        // Handle constraints arising from one always-X index and conditional occurrences
        if (always_indices.size() == 1) {
            for (const auto& occ : conditional_occurrences) {
                int i = get<0>(occ); // Index of the conditional occurrence
                int k = get<1>(occ); // Option index (0 or 1) for this conditional occurrence
                int var_i = i; // Variable index corresponding to U_i
                
                int lit_to_make_true; // The literal that must be true due to the constraint
                // If choosing option k for index i leads to conflict with the always-X index, we must NOT choose option k.
                if (k == 0) { 
                    // Must not choose option 0 => L_i must be false => NOT L_i must be true
                    lit_to_make_true = 2 * var_i + 1; 
                } else { // k == 1
                    // Must not choose option 1 => L_i must be true => L_i must be true
                    lit_to_make_true = 2 * var_i; 
                }
                // Force this literal to be true by adding clause (lit OR lit)
                ts.add_clause(lit_to_make_true, lit_to_make_true); 
            }
        }

        // Handle constraints among multiple conditional occurrences ("at most one" constraint)
        if (always_indices.empty() && conditional_occurrences.size() >= 2) {
            int m = conditional_occurrences.size(); // Number of conditional occurrences for string 'str'
            
            int first_aux_var_idx = current_vars_count; // Starting index for auxiliary variables for this group
            ts.resize(current_vars_count + m); // Ensure 2-SAT solver has space for m new auxiliary variables
            
            vector<int> literals; // Literals representing the choice leading to the conditional occurrence
            literals.reserve(m);
            for(const auto& occ : conditional_occurrences) {
                int i = get<0>(occ);
                int k = get<1>(occ); 
                int var_i = i;
                // Determine the literal corresponding to choosing option k for index i
                if (k == 0) { // Choosing option 0 corresponds to L_i being true
                    literals.push_back(2 * var_i); 
                } else { // Choosing option 1 corresponds to L_i being false (NOT L_i being true)
                    literals.push_back(2 * var_i + 1);
                }
            }

            // Implement "at most one" constraint using auxiliary variables s_1, ..., s_m
            // s_j represents variable first_aux_var_idx + j - 1
            // literal s_j is 2 * (first_aux_var_idx + j - 1)

            int s_prev_lit = 2 * first_aux_var_idx; // Positive literal for s_1

            // Base case: Clause 1 (NOT Lit_1 OR s_1)
            ts.add_clause(ts.neg(literals[0]), s_prev_lit);

            // Recursive step for j = 2 to m (indices 1 to m-1 in loop)
            for (int j = 1; j < m; ++j) {
                // s_{j+1} corresponds to variable first_aux_var_idx + j
                int s_curr_lit = 2 * (first_aux_var_idx + j); // Positive literal for s_{j+1}
                int Lit_j = literals[j]; // Literal for the j-th conditional occurrence (Lit_{j+1} in formula)

                // Add clauses based on the standard encoding for "at most one":
                // Clause 2: (NOT s_j OR s_{j+1})  equivalent to (s_j -> s_{j+1})
                ts.add_clause(ts.neg(s_prev_lit), s_curr_lit); 
                // Clause 3: (NOT Lit_{j+1} OR s_{j+1}) equivalent to (Lit_{j+1} -> s_{j+1})
                ts.add_clause(ts.neg(Lit_j), s_curr_lit);       
                // Clause 4: (NOT Lit_{j+1} OR NOT s_j) equivalent to NOT (Lit_{j+1} AND s_j)
                ts.add_clause(ts.neg(Lit_j), ts.neg(s_prev_lit)); 

                s_prev_lit = s_curr_lit; // Update s_prev literal for the next iteration
            }
            current_vars_count += m; // Increment total variable count
        }
    }
    
    vector<bool> assignment; // Vector to store the resulting truth assignment
    // Solve the 2-SAT instance
    if (ts.solve(assignment)) {
        // If satisfiable, print the chosen pairs (S_i, T_i) based on the assignment
        for (int i = 0; i < N; ++i) {
            // assignment[i] true => L_i true => choose option 0 (lengths 1, 2)
            // assignment[i] false => L_i false => choose option 1 (lengths 2, 1)
            int choice = assignment[i] ? 0 : 1; 
            cout << options[i][choice].first << " " << options[i][choice].second << "\n";
        }
    } else {
        // If unsatisfiable, print "Impossible"
        cout << "Impossible" << endl;
    }

    return 0;
}
0