結果

問題 No.1526 Sum of Mex 2
ユーザー qwewe
提出日時 2025-05-14 13:06:56
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 62 ms / 3,000 ms
コード長 8,170 bytes
コンパイル時間 776 ms
コンパイル使用メモリ 80,092 KB
実行使用メモリ 7,844 KB
最終ジャッジ日時 2025-05-14 13:08:39
合計ジャッジ時間 3,222 ms
ジャッジサーバーID
(参考情報)
judge5 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 32
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

using namespace std;

// Use long long for sums and potentially large values
typedef long long ll;

// Define a large enough value to represent infinity for the initial minimum comparison.
// Needs to be larger than any possible position value (which is N <= 10^5).
// Using 10^9 + 7, a common large prime, is clearly sufficient.
const ll INF = 1e9 + 7; 

// Structure for segment tree nodes
struct Node {
    ll min_val; // Stores the minimum value in the range [start, end] covered by this node
};

// Global variables
vector<ll> p; // Base array (conceptually) storing last seen positions p_0, ..., p_N. p[0] = r, p[i] = last_pos(i, r) for i > 0. We don't actually need to store p explicitly since the segment tree holds the values.
vector<Node> tree; // Segment tree array
int N; // Length of the input array A
int base_size; // The number of logical indices we need to cover: 0 to N, so N+1 indices.
int tree_nodes_allocated_size; // Number of leaves in the segment tree (smallest power of 2 >= base_size)
int tree_array_size; // Actual size allocated for the segment tree array (usually 2 * tree_nodes_allocated_size)

// Function to build the segment tree
// node: current node index in tree array (1-based indexing is common)
// start, end: range of indices covered by this node in the conceptual base array
void build(int node, int start, int end) {
    if (start == end) {
        // Leaf node initialization. If index 'start' is within the logical range [0, N], 
        // initialize with p[start] (which is 0 initially). Otherwise, use 0 for padding leaves.
        if (start < base_size) {
             // Initially all last positions are 0.
             tree[node] = {0}; 
        } else {
             // This part handles indices beyond N used for padding to power of 2 size
             tree[node] = {0}; // Padding leaves are initialized to 0
        }
    } else {
        int mid = start + (end - start) / 2;
        // Recursively build left and right children
        build(2 * node, start, mid);
        build(2 * node + 1, mid + 1, end);
        // Combine results: the minimum value in this node is the minimum of its children
        tree[node] = {min(tree[2 * node].min_val, tree[2 * node + 1].min_val)};
    }
}

// Function to update a value in the segment tree (point update)
// node: current node index
// start, end: range covered by this node
// idx: index in the conceptual base array p to update (0 <= idx <= N)
// val: new value (the position r)
void update(int node, int start, int end, int idx, ll val) {
    // If index is outside the node's range, return. This handles recursive calls correctly.
    if (start > idx || end < idx) {
        return;
    }

    // Leaf node corresponds to idx
    if (start == end) {
        tree[node].min_val = val;
    } else {
        // Recursive step: decide whether to go left or right
        int mid = start + (end - start) / 2;
        if (idx <= mid) { // Check if index is within left child range
            update(2 * node, start, mid, idx, val);
        } else { // Index must be in right child range
            update(2 * node + 1, mid + 1, end, idx, val);
        }
        // After update in child, update current node's minimum based on children
        tree[node].min_val = min(tree[2 * node].min_val, tree[2 * node + 1].min_val);
    }
}

// The custom query function to compute the sum T_r = sum_{k=1}^{N+1} min(p_0, ..., p_{k-1})
// node: current node index
// start, end: range of indices covered by this node in the conceptual base array
// current_min: the minimum value found in p[0...start-1] passed down from parent/ancestor calls
ll query_sum(int node, int start, int end, ll current_min) {
    
    // Clip the current node's range [start, end] to the relevant logical range [0, N].
    // Nodes/parts of nodes covering indices > N are irrelevant to the sum calculation.
    if (start >= base_size) {
         return 0; // This entire node range is outside [0, N]
    }
    int actual_end = min(end, base_size - 1); // Consider only indices up to N

    // Optimization: If the minimum value in this node's range [start, end] is already >= current_min,
    // then for all k such that k-1 is in [start, actual_end], the prefix minimum min(p_0, ..., p_{k-1})
    // will be determined by values before index start, which is captured by current_min.
    // The contribution to the total sum for this range is (number of relevant elements) * current_min.
    // The number of relevant elements is (actual_end - start + 1).
    if (tree[node].min_val >= current_min) {
        return (ll)(actual_end - start + 1) * current_min;
    }
    
    // Base case: Leaf node
    if (start == end) {
        // Check if this leaf index is within the valid range [0, N]
        if (start < base_size) {
            // The value at this leaf is p[start]. The prefix minimum up to index start is min(current_min, p[start]).
            // This corresponds to the term for k = start + 1 in the sum.
             return min(tree[node].min_val, current_min);
        } else {
             return 0; // Leaf index outside [0, N], contributes 0
        }
    }

    // Recursive step: Query left and right children
    int mid = start + (end - start) / 2;
    
    // Query left child. The current_min remains the same.
    ll left_sum = query_sum(2 * node, start, mid, current_min);
    
    // For the right child query, the new current_min needs to incorporate the minimum from the left child's range [start, mid].
    // This minimum is stored in the left child node `tree[2*node]`.
    ll new_current_min = min(current_min, tree[2 * node].min_val); 
    
    // Query right child with the updated current_min.
    ll right_sum = query_sum(2 * node + 1, mid + 1, end, new_current_min);
    
    // Total sum for this node's range is the sum from left and right children.
    return left_sum + right_sum;
}


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

    cin >> N;

    vector<int> A(N + 1); // Input array A (1-based indexing for problem statement)
    for (int i = 1; i <= N; ++i) {
        cin >> A[i];
    }

    // Initialize p array conceptually. We don't need the actual array p.
    // p size needed is N+1 to store indices 0 to N.
    base_size = N + 1; 
    
    // Calculate required size for segment tree leaves (power of 2)
    tree_nodes_allocated_size = 1;
    while (tree_nodes_allocated_size < base_size) {
        tree_nodes_allocated_size *= 2;
    }
    // Total nodes in segment tree array is typically 2*leaves - 1, use 2*leaves for 1-based index safety.
    tree_array_size = 2 * tree_nodes_allocated_size;
    tree.resize(tree_array_size);

    // Build the segment tree covering range [0, tree_nodes_allocated_size - 1]
    // Initial values are all 0 representing initial last positions.
    build(1, 0, tree_nodes_allocated_size - 1); 

    ll total_sum = 0; // Use long long for the final answer

    // Iterate through r from 1 to N, processing the array element A[r]
    for (int r = 1; r <= N; ++r) {
        // Update p[0] to r. This represents the current right endpoint r.
        // Updates operate on the segment tree covering the full allocated range [0, tree_nodes_allocated_size - 1]
        update(1, 0, tree_nodes_allocated_size - 1, 0, r);
        
        // Update p[A[r]] to r. A[r] is the value at index r. Use it as index into p array.
        // Constraint: 1 <= A[i] <= N. So A[r] is a valid index for p[1...N].
        update(1, 0, tree_nodes_allocated_size - 1, A[r], r);

        // Compute Tr = sum_{k=1}^{N+1} min(p_0, ..., p_{k-1}) using the custom query
        // Start query from root node (index 1), covering the full allocated range [0, tree_nodes_allocated_size - 1].
        // The query function internally handles restricting calculations to the logical range [0, N].
        ll Tr = query_sum(1, 0, tree_nodes_allocated_size - 1, INF);
        
        // Add Tr to the total sum
        total_sum += Tr;
    }

    // Output the final total sum
    cout << total_sum << endl;

    return 0;
}
0