結果

問題 No.1020 Reverse
ユーザー qwewe
提出日時 2025-05-14 13:10:02
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 144 ms / 2,000 ms
コード長 7,054 bytes
コンパイル時間 955 ms
コンパイル使用メモリ 107,616 KB
実行使用メモリ 13,056 KB
最終ジャッジ日時 2025-05-14 13:11:18
合計ジャッジ時間 3,080 ms
ジャッジサーバーID
(参考情報)
judge4 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 15
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <random> // Required for std::mt19937 and std::random_device
#include <chrono> // For a better seed based on time

// Use Mersenne Twister for generating priorities. Provides better randomness than rand().
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

// Define the Node structure for the Treap
struct Node {
    char key; // The character stored in this node
    unsigned int priority; // Random priority for maintaining heap property
    int size; // Size of the subtree rooted at this node (number of nodes)
    bool reversed; // Lazy propagation flag for reversal operation
    Node *l, *r; // Pointers to left and right children

    // Constructor for creating a new node
    Node(char k) : key(k), priority(rng()), size(1), reversed(false), l(nullptr), r(nullptr) {}
    
    // Destructor (optional, generally omitted in competitive programming for speed)
    // ~Node() { delete l; delete r; } 
};

// Helper function to get the size of a subtree (handles nullptr case)
int get_size(Node* t) {
    return t ? t->size : 0;
}

// Updates the size of node 't' based on the sizes of its children
void update_size(Node* t) {
    if (t) {
        t->size = get_size(t->l) + get_size(t->r) + 1;
    }
}

// Pushes the lazy 'reversed' tag down to children
// This function must be called before accessing children of a node potentially marked as reversed
void push(Node* t) {
    if (t && t->reversed) {
        t->reversed = false; // Clear the flag on the current node
        std::swap(t->l, t->r); // Swap children pointers physically reverses the order
        // Propagate the reversed state to children by toggling their flags
        if (t->l) t->l->reversed ^= true; 
        if (t->r) t->r->reversed ^= true;
    }
}

// Splits the Treap 't' into two Treaps 'l' and 'r'.
// 'l' will contain the first 'k' elements of the sequence represented by 't'.
// 'r' will contain the remaining elements.
// 'k' is the count of elements for the left part.
void split(Node* t, Node*& l, Node*& r, int k) {
    // Base case: If the treap is empty, both resulting treaps are empty.
    if (!t) {
        l = r = nullptr;
        return;
    }
    
    push(t); // Process lazy tags before operating on the node or its children.
    
    int left_size = get_size(t->l); // Get the size of the left subtree

    // Determine if the split point is in the left or right subtree
    if (k <= left_size) { 
        // Split point is within the left subtree.
        // The current node 't' and its right subtree belong to the right part 'r'.
        split(t->l, l, t->l, k); // Recursively split the left child. The resulting left part is 'l', right part updates t->l.
        r = t; 
    } else {
        // Split point is within the right subtree.
        // The current node 't' and its left subtree belong to the left part 'l'.
        // Adjust 'k' for the recursive call on the right subtree: need k - left_size - 1 elements from it.
        split(t->r, t->r, r, k - left_size - 1); 
        l = t; 
    }
    update_size(t); // Update the size of 't' after potential structural changes.
}

// Merges two Treaps 'l' and 'r' into a single Treap 't'.
// Assumes that all keys (implicitly, indices) in 'l' are less than all keys in 'r'.
void merge(Node*& t, Node* l, Node* r) {
    push(l); // Process lazy tags before merging.
    push(r);
    
    // Base case: If one treap is empty, the result is the other treap.
    if (!l || !r) {
        t = l ? l : r;
    } else if (l->priority > r->priority) { 
        // If 'l' has higher priority, it becomes the root.
        // Recursively merge 'r' into the right subtree of 'l'.
        merge(l->r, l->r, r);
        t = l;
    } else {
        // If 'r' has higher priority (or equal), it becomes the root.
        // Recursively merge 'l' into the left subtree of 'r'.
        merge(r->l, l, r->l);
        t = r;
    }
    update_size(t); // Update the size of the resulting root 't'.
}

// Performs an inorder traversal of the Treap 't' and appends characters to 'res' string.
// This reconstructs the string represented by the Treap.
void inorder_traversal(Node* t, std::string& res) {
    if (!t) return;
    push(t); // Ensure lazy tags are processed before visiting children.
    inorder_traversal(t->l, res); // Visit left child
    res += t->key; // Append current node's character
    inorder_traversal(t->r, res); // Visit right child
}

// Optional: function to delete the treap nodes to free memory (usually skipped in CP)
void delete_treap(Node* t) {
    if (!t) return;
    delete_treap(t->l);
    delete_treap(t->r);
    delete t;
}

int main() {
    // Optimize C++ standard streams for faster I/O.
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);
    
    int N, K; // Read N (string length) and K (substring length for reversal)
    std::cin >> N >> K;
    std::string S; // Read the input string
    std::cin >> S;

    Node* root = nullptr; // Initialize root of the Treap as null

    // Build the initial Treap from the input string S.
    // Each character becomes a node, merged sequentially. O(N log N) build time.
    for (char c : S) {
        merge(root, root, new Node(c));
    }

    // Perform the sequence of reversal operations.
    // Loop runs N-K+1 times, from i=1 to N-K+1.
    for (int i = 1; i <= N - K + 1; ++i) {
        Node *T1, *T2, *T3; // Temporary pointers for split parts
        
        // The problem uses 1-based indexing [i, i+K-1].
        // In 0-based indexing, this corresponds to [i-1, i+K-2].
        // We need to extract the substring of length K starting at index i-1.
        
        // Split the treap `root` into `T1` and `T2_T3`.
        // `T1` contains the first `i-1` elements (indices 0 to i-2).
        // `T2_T3` contains the rest (indices i-1 to N-1).
        split(root, T1, T2, i - 1); 
        
        // Split `T2_T3` into `T2` and `T3`.
        // `T2` contains the first `K` elements (indices i-1 to i+K-2). This is the target substring.
        // `T3` contains the remaining elements (indices i+K-1 to N-1).
        split(T2, T2, T3, K);

        // Apply the reverse operation lazily to the extracted substring `T2`.
        if (T2) {
            T2->reversed ^= true; // Toggle the reversed flag. Actual reversal happens on push.
        }

        // Merge the three parts back together in the correct order.
        merge(root, T1, T2); // Merge T1 and the (potentially reversed) T2.
        merge(root, root, T3); // Merge the result with T3.
    }

    // Retrieve the final string by performing an inorder traversal of the modified Treap.
    std::string final_S = "";
    final_S.reserve(N); // Reserve capacity for efficiency.
    inorder_traversal(root, final_S);
    
    // Print the resulting string.
    std::cout << final_S << std::endl;

    // Optional cleanup (can be important for very large N or strict memory limits)
    // delete_treap(root); 
    
    return 0;
}
0