結果
| 問題 |
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 |
ソースコード
#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;
}
qwewe