結果

問題 No.1612 I hate Construct a Palindrome
ユーザー qwewe
提出日時 2025-05-14 13:18:20
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
WA  
実行時間 -
コード長 10,653 bytes
コンパイル時間 1,161 ms
コンパイル使用メモリ 96,168 KB
実行使用メモリ 17,228 KB
最終ジャッジ日時 2025-05-14 13:19:25
合計ジャッジ時間 10,909 ms
ジャッジサーバーID
(参考情報)
judge1 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 28 WA * 6 TLE * 2
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <vector>
#include <algorithm>
#include <tuple>

using namespace std;

// Using type aliases for clarity and brevity
using VI = vector<int>;         // Vector of integers
using VC = vector<char>;        // Vector of characters
using PII = pair<int, int>;     // Pair of integers
// Tuple to represent an edge in the adjacency list: {neighbor_vertex, edge_label, edge_index}
template<typename T1, typename T2, typename T3>
using Tuple3 = tuple<T1, T2, T3>; 

const int MAXN_glob = 100005; // Maximum number of vertices + 1
const int MAXM_glob = 200005; // Maximum number of edges + 1

// Adjacency list representation of the graph
// adj_glob[u] stores a list of tuples {v, label, edge_idx} for edges connected to vertex u
vector<Tuple3<int, char, int>> adj_glob[MAXN_glob]; 
// Array to store edge information (u, v, label) indexed by edge ID (1 to M)
// This allows quick lookup of edge label given its index
Tuple3<int, int, char> edge_info_glob[MAXM_glob + 1]; 
int N_glob, M_glob; // Global variables for number of vertices N and edges M


/**
 * @brief Checks if a sequence of characters forms a palindrome.
 * 
 * @param labels The vector of characters representing the sequence.
 * @return true if the sequence is a palindrome, false otherwise.
 */
bool is_palindrome(const VC& labels) {
    int k = labels.size();
    // An empty string or a string with one character is considered a palindrome.
    if (k <= 1) return true; 
    // Compare characters from the beginning and end moving inwards.
    for (int i = 0; i < k / 2; ++i) {
        if (labels[i] != labels[k - 1 - i]) {
            return false; // If any pair doesn't match, it's not a palindrome.
        }
    }
    return true; // All pairs matched, it's a palindrome.
}


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

    // Read the number of vertices N and edges M from input.
    cin >> N_glob >> M_glob;

    // Read M edges from input and populate the adjacency list and edge info array.
    for (int i = 1; i <= M_glob; ++i) {
        int u, v;
        char c;
        cin >> u >> v >> c; // Read vertices u, v and edge label c.
        // Add edge to adjacency list for both u and v (since it's an undirected graph).
        adj_glob[u].emplace_back(v, c, i);
        adj_glob[v].emplace_back(u, c, i);
        // Store edge info (u, v, label) associated with edge index i.
        edge_info_glob[i] = make_tuple(u, v, c);
    }

    // Perform Breadth-First Search (BFS) starting from vertex 1 to find a shortest path to vertex N.
    queue<int> q; // Queue for BFS
    VI dist(N_glob + 1, -1); // distance[v] stores shortest distance from vertex 1 to v. Initialized to -1 (unvisited).
    // parent[v] stores {previous_node, edge_index_used} to reach v in the shortest path from 1.
    vector<PII> parent(N_glob + 1, {-1, -1}); 

    q.push(1); // Start BFS from vertex 1.
    dist[1] = 0; // Distance from source to itself is 0.

    int final_node = -1; // Variable to confirm if vertex N was reached.

    // BFS loop
    while (!q.empty()) {
        int u = q.front();
        q.pop();

        // If we reached the target vertex N, stop BFS.
        if (u == N_glob) {
            final_node = N_glob;
            break; 
        }

        // Explore neighbors of vertex u.
        for (auto& edge : adj_glob[u]) {
            int v = get<0>(edge); // Neighbor vertex
            int edge_idx = get<2>(edge); // Index of the edge (u, v)
            // If neighbor v has not been visited yet (distance is -1).
            if (dist[v] == -1) { 
                dist[v] = dist[u] + 1; // Update distance to v.
                parent[v] = {u, edge_idx}; // Set parent of v for path reconstruction.
                q.push(v); // Add v to the queue to visit later.
            }
        }
    }
    
    // After BFS, check if vertex N was reachable from vertex 1.
    if (final_node == -1) {
        // The problem guarantees the graph is connected, so N should always be reachable if N >= 2.
        // Output -1 if N is unreachable (this should theoretically not happen based on constraints).
        cout << -1 << endl;
        return 0;
    }

    // Reconstruct the shortest path from 1 to N using the parent pointers.
    VI path_v; // Stores the sequence of vertices in the path.
    VI path_e; // Stores the sequence of edge indices used in the path.
    VC path_l; // Stores the sequence of edge labels along the path.
    int curr = N_glob; // Start reconstruction from the target vertex N.
    // Trace back parents until we reach the start vertex 1.
    while (curr != 1) {
        // Defensive check for errors during reconstruction.
        if (parent[curr].first == -1 && curr != 1) {
             cerr << "Error during path reconstruction: Parent indicates start node prematurely." << endl; 
             cout << -1 << endl; return 0; 
        }
        int p_node = parent[curr].first; // Previous node in the path.
        int edge_idx = parent[curr].second; // Index of the edge used to reach curr from p_node.
        path_e.push_back(edge_idx); // Add edge index to path list.
        
        // Retrieve the label of the edge using the stored edge info.
        char label = get<2>(edge_info_glob[edge_idx]);
        path_l.push_back(label); // Add label to path list.

        path_v.push_back(curr); // Add current vertex to path list (will be reversed).
        curr = p_node; // Move to the previous vertex.
    }
    path_v.push_back(1); // Add the starting vertex 1 to the path list.
    
    // The path was reconstructed backwards (from N to 1), so reverse the sequences.
    reverse(path_v.begin(), path_v.end()); // path_v now stores vertices 1...N
    reverse(path_e.begin(), path_e.end()); // path_e now stores edge indices along 1...N
    reverse(path_l.begin(), path_l.end()); // path_l now stores labels along 1...N

    // Check if the label sequence of the found shortest path is a palindrome.
    if (!is_palindrome(path_l)) {
        // If it's not a palindrome, this path is a valid solution. Output it.
        cout << path_e.size() << endl; // Output path length (number of edges).
        for(int edge_idx : path_e) {
            cout << edge_idx << endl; // Output each edge index on a new line.
        }
        return 0; // Solution found, terminate program.
    }

    // If the shortest path is a palindrome, we need to find a different path.
    // Strategy: Try modifying the shortest path by inserting a small cycle (u -> w -> u).
    int k = path_e.size(); // Length of the shortest path.
    // Iterate through each vertex v_i on the shortest path (vertices are indexed 0 to k).
    for (int i = 0; i < path_v.size(); ++i) { 
        int u = path_v[i]; // Current vertex v_i on the path.
        
        // Iterate through all edges incident to vertex u.
        for (auto& edge : adj_glob[u]) { 
            // int w = get<0>(edge); // Neighbor vertex w, not strictly needed here.
            char c_prime = get<1>(edge); // Label of the edge (u, w).
            int e_prime_idx = get<2>(edge); // Index of the edge (u, w).

            // Construct the label sequence for the new path P' which includes the cycle u -> w -> u.
            // The new path essentially goes 1... -> u -> w -> u -> ...N.
            VC new_path_l; // Vector to store the labels of the modified path.
            new_path_l.reserve(k + 2); // Reserve space for efficiency (length will be k+2).

            // Part 1: Add labels from the original path before vertex v_i (edges e_1 to e_i).
            // These correspond to labels path_l[0] through path_l[i-1].
            for (int j = 0; j < i; ++j) {
                new_path_l.push_back(path_l[j]);
            }
            // Part 2: Insert the labels corresponding to the cycle u -> w -> u.
            // Both traversals use the same edge (u, w), so the label c_prime appears twice.
            new_path_l.push_back(c_prime);
            new_path_l.push_back(c_prime);
            // Part 3: Add labels from the original path starting from vertex v_i (edges e_{i+1} to e_k).
            // These correspond to labels path_l[i] through path_l[k-1].
            for (int j = i; j < k; ++j) {
                new_path_l.push_back(path_l[j]);
            }
            
            // Check if the modified path P' has a non-palindromic label sequence.
            if (!is_palindrome(new_path_l)) {
                 // If non-palindromic, construct its edge index sequence.
                 VI new_path_e; // Vector to store edge indices of the modified path.
                 new_path_e.reserve(k + 2); // Reserve space for efficiency.

                 // Edges corresponding to Part 1 (before index i).
                 for(int j=0; j<i; ++j) {
                     new_path_e.push_back(path_e[j]);
                 }
                 // Edges corresponding to Part 2 (the cycle u -> w -> u).
                 // Edge index e_prime_idx is used twice.
                 new_path_e.push_back(e_prime_idx); 
                 new_path_e.push_back(e_prime_idx); 
                 // Edges corresponding to Part 3 (from index i onwards).
                 for(int j=i; j < k; ++j) {
                      new_path_e.push_back(path_e[j]);
                 }
                 
                 // Check if the new path length exceeds the constraint 2*N.
                 // The length is k+2. Since k <= N-1 (shortest path), k+2 <= N+1.
                 // For N >= 1, N+1 <= 2N. Constraint is always satisfied if N>=1. Problem states N>=2.
                 if (new_path_e.size() > 2 * N_glob) {
                    // This condition should technically not be met given the logic above, but included as a safeguard.
                    continue; // Skip if path somehow exceeds length limit.
                 }

                 // Output the resulting path edge indices.
                 cout << new_path_e.size() << endl; // Output path length k+2.
                 for(int edge_idx : new_path_e) {
                     cout << edge_idx << endl; // Output each edge index.
                 }
                 return 0; // Solution found, terminate program.
            }
        }
    }

    // If the program reaches this point, it means the shortest path was a palindrome,
    // and inserting any single cycle u -> w -> u at any vertex u on the path
    // also resulted in a palindrome. In this case, we conclude no suitable path
    // was found meeting the criteria (non-palindromic, length <= 2N).
    cout << -1 << endl;

    return 0;
}
0