結果

問題 No.919 You Are A Project Manager
ユーザー qwewe
提出日時 2025-05-14 13:14:48
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 41 ms / 3,000 ms
コード長 10,422 bytes
コンパイル時間 1,593 ms
コンパイル使用メモリ 115,220 KB
実行使用メモリ 7,844 KB
最終ジャッジ日時 2025-05-14 13:15:51
合計ジャッジ時間 4,256 ms
ジャッジサーバーID
(参考情報)
judge3 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 55
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <vector> 
#include <cmath> // For std::log2

// Define a structure for the nodes of the persistent segment tree
struct Node {
    long long count; // Stores the number of elements in the range represented by this node
    int left, right; // Indices of the left and right children in the 'tree' vector (node pool)
    Node() : count(0), left(0), right(0) {} // Default constructor
};

// Global vector to store all nodes of the segment trees (node pool)
std::vector<Node> tree; 
// Global vector to store the root indices of different versions of the segment tree
// roots[i] stores the root for the prefix A[0...i-1]
std::vector<int> roots; 
// Global index for allocating new nodes from the pool
int node_pool_idx = 1; // Start idx from 1, index 0 represents a null node

/**
 * @brief Updates the persistent segment tree by adding an element.
 * 
 * Creates a new version of the tree based on a previous version 'prev_root'
 * by incrementing the count for the element at position 'pos' (in the compressed value range).
 * The segment tree operates on the value range [L, R].
 * 
 * @param prev_root Index of the root node of the previous version.
 * @param L The lower bound of the current node's value range.
 * @param R The upper bound of the current node's value range.
 * @param pos The compressed rank (position) of the value being added.
 * @return Index of the root node of the new version.
 */
int update(int prev_root, int L, int R, int pos) {
    int new_root = node_pool_idx++; // Allocate a new node index
    
    // Dynamic resizing of the node pool vector if it runs out of space
    if (new_root >= tree.size()) {
         // Increase size; using 1.5x growth factor is common for amortized efficiency
         tree.resize(tree.size() * 1.5 + 1); 
    }

    // Copy data from the corresponding node in the previous version
    tree[new_root] = tree[prev_root]; 
    tree[new_root].count++; // Increment the count because we added an element in this path

    // If this is a leaf node (range contains single value), return the new node index
    if (L == R) { 
        return new_root;
    }

    int mid = L + (R - L) / 2; // Calculate midpoint, avoiding potential overflow
    // Recursively update the appropriate child (left or right) based on 'pos'
    if (pos <= mid) { // If the position is in the left half of the range
        tree[new_root].left = update(tree[prev_root].left, L, mid, pos);
    } else { // If the position is in the right half
        tree[new_root].right = update(tree[prev_root].right, mid + 1, R, pos);
    }
    return new_root; // Return index of the newly created root node for this version
}

/**
 * @brief Queries the persistent segment tree to find the rank of the k-th smallest element.
 * 
 * Finds the rank (index in the compressed value range) of the k-th smallest element
 * within the subarray represented by the difference between tree versions 'r2' and 'r1'.
 * The segment tree operates on the value range [L, R].
 * 
 * @param r1 Index of the root node for the version representing the prefix *before* the query range.
 * @param r2 Index of the root node for the version representing the prefix *at the end* of the query range.
 * @param L The lower bound of the current node's value range.
 * @param R The upper bound of the current node's value range.
 * @param k The rank of the element to find (e.g., k=1 for minimum, k=N/2 for median-like).
 * @return The compressed rank (index) of the k-th smallest element.
 */
int query(int r1, int r2, int L, int R, int k) {
    // Base case: If we reach a leaf node, its range [L,R] has L=R. This L is the rank.
    if (L == R) { 
        return L; 
    }
    
    // Calculate the count of elements in the left subtree within the specified range.
    // This is the difference between counts in version r2 and version r1.
    long long left_count = tree[tree[r2].left].count - tree[tree[r1].left].count;
    int mid = L + (R - L) / 2; // Calculate midpoint

    // Decide whether to search in the left or right subtree
    if (k <= left_count) { // If the k-th element is within the left subtree's count
        return query(tree[r1].left, tree[r2].left, L, mid, k);
    } else { // If the k-th element is within the right subtree's count
        // Adjust k for the search in the right subtree: we are now looking for the (k - left_count)-th element in the right part.
        return query(tree[r1].right, tree[r2].right, mid + 1, R, k - left_count);
    }
}


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

    int N; // Number of programmers
    std::cin >> N;

    std::vector<long long> A(N); // Stores motivation values
    std::vector<long long> unique_vals_vec; // Used for coordinate compression
    unique_vals_vec.reserve(N); // Reserve capacity to potentially avoid reallocations
    for (int i = 0; i < N; ++i) {
        std::cin >> A[i];
        unique_vals_vec.push_back(A[i]);
    }

    // Coordinate Compression: Map large motivation values to smaller integer ranks [0, D-1]
    std::sort(unique_vals_vec.begin(), unique_vals_vec.end());
    // Remove duplicate values
    unique_vals_vec.erase(std::unique(unique_vals_vec.begin(), unique_vals_vec.end()), unique_vals_vec.end());

    std::map<long long, int> val_to_rank; // Map original value to its rank
    std::vector<long long> rank_to_val(unique_vals_vec.size()); // Map rank back to original value
    for (int i = 0; i < unique_vals_vec.size(); ++i) {
        val_to_rank[unique_vals_vec[i]] = i;
        rank_to_val[i] = i; // Store original value at index corresponding to its rank
    }
    // Corrected map: rank_to_val should store original value
    for (int i = 0; i < unique_vals_vec.size(); ++i) {
        rank_to_val[i] = unique_vals_vec[i];
    }


    int D = unique_vals_vec.size(); // Number of distinct motivation values
    
    // Estimate initial size for the segment tree node pool. N * logN nodes approximately.
    // Add a buffer factor for safety.
    int initial_tree_size = std::max((int)(N * (std::log2(N+1) + 4)), 100); // Increased buffer
    tree.resize(initial_tree_size);
    roots.resize(N + 1); // Need N+1 roots for prefixes of length 0 to N

    tree[0] = Node(); // Initialize the null node (node 0)
    roots[0] = 0; // roots[0] corresponds to the empty prefix

    // Build the persistent segment tree versions for all prefixes A[0...i]
    for (int i = 0; i < N; ++i) {
        // roots[i+1] is built upon roots[i] by adding element A[i]
        roots[i+1] = update(roots[i], 0, D - 1, val_to_rank[A[i]]);
    }

    long long max_total_productivity = 0; // Stores the overall maximum productivity found

    // Iterate through all possible team sizes K from 1 to N
    for (int K = 1; K <= N; ++K) {
        int M = N / K; // Maximum number of teams that can be formed consecutively from one end
        if (M == 0) continue; // If N < K, we cannot form any team of size K

        // L[p] stores the total productivity of the first p teams taken from the left
        std::vector<long long> L(M + 1, 0); 
        // R[q] stores the total productivity of the first q teams taken from the right
        std::vector<long long> R(M + 1, 0); 

        // Determine the rank 'k_th' needed for the median calculation based on K
        // Problem statement definition: B[K/2] if K even, B[(K+1)/2] if K odd.
        // This corresponds to the k-th smallest element where k = K/2 for even K, (K+1)/2 for odd K.
        int k_th = (K % 2 == 1) ? (K + 1) / 2 : K / 2;

        // Precompute L[p] values: Sum of productivities for teams taken from the left
        for (int p = 1; p <= M; ++p) {
            int start_idx = (p - 1) * K;       // 0-based start index of the p-th block from left
            int end_idx = p * K - 1;         // 0-based end index of the p-th block from left
            // Query the k-th element's rank in the range [start_idx, end_idx]
            // Using roots[start_idx] (version before range) and roots[end_idx + 1] (version at end of range)
            int rank = query(roots[start_idx], roots[end_idx + 1], 0, D - 1, k_th);
            long long median_val = rank_to_val[rank]; // Map rank back to original value
            // Add productivity of this team to the previous sum
            L[p] = L[p-1] + (long long)K * median_val;
        }

        // Precompute R[q] values: Sum of productivities for teams taken from the right
        for (int q = 1; q <= M; ++q) {
            int start_idx = N - q * K;        // 0-based start index of the q-th block from right
            int end_idx = N - (q - 1) * K - 1; // 0-based end index of the q-th block from right
            // Query the k-th element's rank in this range
            int rank = query(roots[start_idx], roots[end_idx + 1], 0, D - 1, k_th);
            long long median_val = rank_to_val[rank]; // Map rank back to original value
            // Add productivity of this team to the previous sum
            R[q] = R[q-1] + (long long)K * median_val;
        }

        // Compute MaxR[k] = max_{0 <= i <= k} R[i] using prefix maximums
        std::vector<long long> MaxR(M + 1);
        MaxR[0] = R[0]; // Base case: MaxR[0] = R[0] = 0
        for (int q = 1; q <= M; ++q) {
           // MaxR[q] is the maximum value found in R[0]...R[q]
           MaxR[q] = std::max(MaxR[q-1], R[q]);
        }
        
        // Find the maximum possible total productivity for the current K
        // Using the O(M) approach: max_{0 <= p <= M} (L[p] + MaxR[M-p])
        long long current_K_max_prod = 0; // Max productivity for this K, initialized to 0 (option of forming no teams)
        for (int p = 0; p <= M; ++p) {
             int max_q_idx = M - p; // The maximum index for q such that p + q <= M
             if (max_q_idx >= 0) { // Check if the index for MaxR is valid
                 // Calculate potential total productivity and update maximum for this K
                 current_K_max_prod = std::max(current_K_max_prod, L[p] + MaxR[max_q_idx]);
             }
        }

        // Update the overall maximum productivity found across all K values
        max_total_productivity = std::max(max_total_productivity, current_K_max_prod);
    }

    // Print the final result
    std::cout << max_total_productivity << std::endl;

    return 0;
}
0