結果
問題 |
No.1496 不思議な数え上げ
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:16:34 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 10,226 bytes |
コンパイル時間 | 1,260 ms |
コンパイル使用メモリ | 106,468 KB |
実行使用メモリ | 33,836 KB |
最終ジャッジ日時 | 2025-05-14 13:18:09 |
合計ジャッジ時間 | 7,724 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 3 TLE * 1 -- * 35 |
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <algorithm> #include <cmath> // Using pragmas or compiler flags to increase stack size might be necessary for large N due to deep recursion. // Example for g++: Compile with -Wl,--stack,268435456 (sets stack size to 256MB) // Example for MSVC: #pragma comment(linker, "/STACK:268435456") using namespace std; // Use long long for sums, counts, and threshold values as they can exceed 32-bit integer limits. typedef long long ll; // Constants for array sizes and logarithm calculations. const int MAXN = 200005; // Maximum N + a small buffer const int LOGN = 18; // Sufficient for N up to 2^18 > 200005. ceil(log2(MAXN)) int N; // Size of the permutation. // Use vectors with size N+1 for 1-based indexing compatibility with problem statement. vector<ll> P; // Stores the permutation P_1...P_N. Using ll is safe, though int might suffice for P values themselves. vector<ll> A; // Stores the threshold values A_1...A_N. Needs ll as values can be large. vector<ll> S; // Stores prefix sums S_0...S_N. S[k] = sum(P_1...P_k). Needs ll. vector<ll> Ans; // Stores the final counts Ans_1...Ans_N. Needs ll as counts can be large. // Sparse table for Range Minimum Query (RMQ). // st[k][i] stores the index of the minimum element in the range P[i...i + 2^k - 1]. vector<vector<int>> st; // Precomputed floor(log base 2) values. log_table[x] = floor(log2(x)). vector<int> log_table; // Precomputes floor(log base 2) values for i from 1 to size. // Used for O(1) calculation of k in RMQ query. // Time complexity: O(size) void compute_log_table(int size) { log_table.resize(size + 1); // log2(1) = 0. Start loop from 2. if (size >= 1) log_table[1] = 0; for (int i = 2; i <= size; ++i) { // Basic property of logarithms: log2(i) = log2(i/2) + 1. Integer division handles floor. log_table[i] = log_table[i / 2] + 1; } } // Builds the sparse table for RMQ using dynamic programming. // Time complexity: O(N log N) void build_sparse_table(int size) { // Initialize sparse table dimensions. LOGN rows, size+1 columns. st.assign(LOGN, vector<int>(size + 1)); // Base case: ranges of length 1 (2^0). The minimum element is the element itself. for (int i = 1; i <= size; ++i) { st[0][i] = i; // Store the index of the minimum element. } // Build table for ranges of length 2^k based on ranges of length 2^(k-1). for (int k = 1; k < LOGN; ++k) { // Iterate through all possible starting positions i for ranges of length 2^k. // The range is [i, i + 2^k - 1]. Ensure the range endpoint does not exceed N. // Maximum starting index i is size - (1 << k) + 1. for (int i = 1; i <= size - (1 << k) + 1; ++i) { // The range [i, i + 2^k - 1] is composed of two overlapping ranges of length 2^(k-1): // First half: [i, i + 2^(k-1) - 1] // Second half: [i + 2^(k-1), i + 2^k - 1] int idx1 = st[k - 1][i]; // Minimum index in the first half. int idx2 = st[k - 1][i + (1 << (k - 1))]; // Minimum index in the second half. // Compare the values P[idx1] and P[idx2] to find the minimum index for the combined range. // Tie-breaking rule: If P[idx1] == P[idx2], prefer the smaller index idx1. This is consistent. if (P[idx1] <= P[idx2]) { st[k][i] = idx1; } else { st[k][i] = idx2; } } } } // Queries the sparse table for the index of the minimum element in the range P[L...R]. // Time complexity: O(1) int query_rmq(int L, int R) { // Handle edge cases: empty or invalid range. Though valid calls shouldn't trigger this. if (L > R) return 0; // Base case: range with a single element. if (L == R) return L; // Calculate k = floor(log2(length of range)). Length is R - L + 1. Use precomputed log table. int k = log_table[R - L + 1]; // The range [L, R] is covered by two ranges of length 2^k that might overlap: // Range 1: [L, L + 2^k - 1] // Range 2: [R - 2^k + 1, R] int idx1 = st[k][L]; // Minimum index in the first range. int idx2 = st[k][R - (1 << k) + 1]; // Minimum index in the second range. // Compare the values at these indices to find the overall minimum index for the range [L, R]. if (P[idx1] <= P[idx2]) { // Apply consistent tie-breaking rule. return idx1; } else { return idx2; } } // Recursive function implementing the divide and conquer strategy. // Processes the range [start, end], finds the minimum element P[k] = i, // counts valid subarrays crossing k, and recursively calls for left/right parts. // Time complexity analysis: Each element is processed when it's the minimum in a range. // The counting part takes O(M log M) where M is the size of the larger half. // Summing over all recursive calls gives O(N log^2 N). void solve(int start, int end) { if (start > end) { // Base case: If the range is empty, do nothing. return; } // Find the index k of the minimum element P[k] within the current range [start, end]. int k = query_rmq(start, end); // The value of this minimum element is i = P[k]. ll i = P[k]; // Calculate the lengths of the left part [start...k] and right part [k...end]. // Note that k itself is included in both conceptual parts for the purpose of defining subarrays crossing k. int lenL = k - start + 1; // Number of elements from start to k, inclusive. int lenR = end - k + 1; // Number of elements from k to end, inclusive. // Optimization: Iterate over the smaller half and perform queries on the data from the larger half. // This is known as the "smaller to larger" trick or similar to centroid decomposition logic. if (lenL <= lenR) { // Case 1: Left part [start...k] is smaller or equal in size. // Collect prefix sums S[R] for R in the right part [k...end]. vector<ll> SR_values; SR_values.reserve(lenR); // Optimize vector capacity. for (int R_idx = k; R_idx <= end; ++R_idx) { SR_values.push_back(S[R_idx]); } // Sort these values to enable efficient binary search (using std::upper_bound). sort(SR_values.begin(), SR_values.end()); ll current_count = 0; // Accumulator for the count of valid subarrays with minimum P[k]=i crossing k. // Iterate through all possible start indices L_idx from start to k. for (int L_idx = start; L_idx <= k; ++L_idx) { // A subarray P[L_idx...R_idx] must satisfy: sum <= A[i]. // Sum = S[R_idx] - S[L_idx - 1]. So, S[R_idx] - S[L_idx - 1] <= A[i]. // Rearranging for querying S[R]: S[R_idx] <= A[i] + S[L_idx - 1]. ll target = A[i] + S[L_idx - 1]; // Use upper_bound to find the first element strictly greater than target. // The number of elements <= target is the distance from the beginning of the sorted vector. auto it = upper_bound(SR_values.begin(), SR_values.end(), target); current_count += (it - SR_values.begin()); } // Add the count computed for this specific minimum value i=P[k] to the global answer array. Ans[i] += current_count; } else { // Case 2: Right part [k...end] is smaller (lenR < lenL). // Collect prefix sums S[L-1] for L in the left part [start...k]. vector<ll> SL_minus_1_values; SL_minus_1_values.reserve(lenL); // Optimize capacity. for (int L_idx = start; L_idx <= k; ++L_idx) { SL_minus_1_values.push_back(S[L_idx - 1]); } // Sort these values for binary search (using std::lower_bound). sort(SL_minus_1_values.begin(), SL_minus_1_values.end()); ll current_count = 0; // Accumulator. // Iterate through all possible end indices R_idx from k to end. for (int R_idx = k; R_idx <= end; ++R_idx) { // Condition: S[R_idx] - S[L_idx - 1] <= A[i]. // Rearranging for querying S[L-1]: S[L_idx - 1] >= S[R_idx] - A[i]. ll target = S[R_idx] - A[i]; // Use lower_bound to find the first element >= target. // The number of elements >= target is the count from this iterator to the end of the vector. auto it = lower_bound(SL_minus_1_values.begin(), SL_minus_1_values.end(), target); current_count += (SL_minus_1_values.end() - it); } // Add the count computed to the global answer array. Ans[i] += current_count; } // Recursively call 'solve' for the subproblems corresponding to ranges strictly to the left and right of k. // Subarrays fully contained within these ranges will be processed by these recursive calls. solve(start, k - 1); // Process the left subproblem. solve(k + 1, end); // Process the right subproblem. } int main() { // Use fast I/O operations. ios_base::sync_with_stdio(false); cin.tie(NULL); // Read input N: the length of the permutation. cin >> N; // Resize vectors based on N. Use N+1 elements for 1-based indexing. P.resize(N + 1); A.resize(N + 1); S.resize(N + 1); Ans.resize(N + 1, 0); // Initialize answer counts to 0. // Read the permutation P_1...P_N. for (int i = 1; i <= N; ++i) { cin >> P[i]; } // Read the threshold values A_1...A_N. for (int i = 1; i <= N; ++i) { cin >> A[i]; } // Compute prefix sums S. S[k] = P_1 + ... + P_k. Set S[0] = 0. S[0] = 0; for (int i = 1; i <= N; ++i) { S[i] = S[i - 1] + P[i]; } // Precompute necessary structures: log table and sparse table for RMQ. compute_log_table(N); // Pass N to size log_table correctly. build_sparse_table(N); // Pass N for correct bounds in sparse table construction. // Initiate the divide and conquer process starting with the full range [1, N]. solve(1, N); // Output the final counts for each i from 1 to N, each on a new line. for (int i = 1; i <= N; ++i) { cout << Ans[i] << "\n"; } return 0; }