Submission #1299846

#TimeUsernameProblemLanguageResultExecution timeMemory
1299846jg86Cake 3 (JOI19_cake3)C++20
100 / 100
1916 ms15316 KiB
/** * JOI 2019 Spring Camp - Cake 3 * * Problem Analysis: * We need to choose M pieces. Let the chosen pieces be sorted by Color: P_1, P_2, ..., P_M. * The beauty is sum(V) - sum(|C_diff|). * The sum of color differences in a circle for sorted colors is 2 * (C_max - C_min). * * Strategy: * 1. Sort all pieces by Color C. * 2. We need to select a range [i, j] in the sorted array such that: * - The pieces i and j are the min and max color boundaries. * - We select M pieces total from the range [i, j]. * - To maximize beauty, we must pick the M pieces with the largest Values V within [i, j]. * 3. The objective function for a fixed range [i, j] (where j >= i + M - 1): * f(i, j) = SumTopM(i, j) - 2 * (C[j] - C[i]) * 4. We use Divide and Conquer Optimization. * solve(i_start, i_end, j_start, j_end) computes the best j for i in [i_start, i_end]. * We maintain a global Segment Tree that tracks the values in the current window [curr_L, curr_R]. * * Complexity: O(N * log^2 N) */ #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; // Structure to represent a piece of cake struct Piece { long long v, c; int original_id; }; int N, M; vector<Piece> pieces; vector<long long> v_coords; // For coordinate compression of V // Segment Tree to maintain counts and sums of V values // We use coordinate compression on V values, so the tree size is N. const int MAXN = 200005; long long seg_count[4 * MAXN]; long long seg_sum[4 * MAXN]; // Add a value (by its compressed index/rank) to the segment tree void update(int node, int start, int end, int idx, int val_cnt, long long val_amount) { if (start == end) { seg_count[node] += val_cnt; seg_sum[node] += val_amount; // If val_cnt is 1, we add real_value; if -1, we subtract return; } int mid = (start + end) / 2; if (idx <= mid) update(node * 2, start, mid, idx, val_cnt, val_amount); else update(node * 2 + 1, mid + 1, end, idx, val_cnt, val_amount); seg_count[node] = seg_count[node * 2] + seg_count[node * 2 + 1]; seg_sum[node] = seg_sum[node * 2] + seg_sum[node * 2 + 1]; } // Query the sum of the k largest values in the segment tree long long query(int node, int start, int end, int k) { if (k <= 0) return 0; if (seg_count[node] <= k) return seg_sum[node]; // Take everything if (start == end) { // We are at a leaf, we need 'k' items from here. // The value at this leaf is seg_sum[node] / seg_count[node] (conceptually) // Since all items at a leaf are identical in compressed value: long long single_val = seg_sum[node] / seg_count[node]; return single_val * k; } int mid = (start + end) / 2; // Right child has larger values (since we compressed V such that larger index = larger V) if (seg_count[node * 2 + 1] >= k) { return query(node * 2 + 1, mid + 1, end, k); } else { return seg_sum[node * 2 + 1] + query(node * 2, start, mid, k - seg_count[node * 2 + 1]); } } // Global pointers for the current range covered by the segment tree int cur_L = 1; int cur_R = 0; // Function to move the segment tree range to [L, R] void move_range(int L, int R) { // Extend or shrink R while (cur_R < R) { cur_R++; // Identify the rank of the V value at cur_R int rank = lower_bound(v_coords.begin(), v_coords.end(), pieces[cur_R].v) - v_coords.begin(); update(1, 0, N - 1, rank, 1, pieces[cur_R].v); } while (cur_R > R) { int rank = lower_bound(v_coords.begin(), v_coords.end(), pieces[cur_R].v) - v_coords.begin(); update(1, 0, N - 1, rank, -1, -pieces[cur_R].v); cur_R--; } // Extend or shrink L while (cur_L > L) { cur_L--; int rank = lower_bound(v_coords.begin(), v_coords.end(), pieces[cur_L].v) - v_coords.begin(); update(1, 0, N - 1, rank, 1, pieces[cur_L].v); } while (cur_L < L) { int rank = lower_bound(v_coords.begin(), v_coords.end(), pieces[cur_L].v) - v_coords.begin(); update(1, 0, N - 1, rank, -1, -pieces[cur_L].v); cur_L++; } } long long max_beauty = -2e18; // Initialize with a very small number // Divide and Conquer function // Computes the optimal j for each i in [i_start, i_end], knowing optimal j is in [j_start, j_end] void solve(int i_start, int i_end, int j_start, int j_end) { if (i_start > i_end) return; int i_mid = (i_start + i_end) / 2; int best_j = -1; long long current_max = -4e18; // We need to iterate j from max(j_start, i_mid + M - 1) to j_end int start_scan = max(j_start, i_mid + M - 1); // Evaluate for i_mid // We iterate j and find the best one for this specific i_mid for (int j = start_scan; j <= j_end; ++j) { move_range(i_mid, j); // Adjust seg tree to [i_mid, j] long long sum_top_m = query(1, 0, N - 1, M); long long beauty = sum_top_m - 2 * (pieces[j].c - pieces[i_mid].c); if (beauty > current_max) { current_max = beauty; best_j = j; } } if (best_j != -1) { max_beauty = max(max_beauty, current_max); } else { // If no valid j found (e.g. range too small), we still need a valid best_j for recursion bounds. // Logic suggests best_j should be at least start_scan. best_j = j_start; } // Recursive calls // For i < i_mid, optimal j is <= best_j solve(i_start, i_mid - 1, j_start, best_j); // For i > i_mid, optimal j is >= best_j solve(i_mid + 1, i_end, best_j, j_end); } int main() { // Fast I/O ios_base::sync_with_stdio(false); cin.tie(NULL); if (!(cin >> N >> M)) return 0; pieces.resize(N + 1); // 1-based indexing for convenience in logic for (int i = 1; i <= N; ++i) { cin >> pieces[i].v >> pieces[i].c; v_coords.push_back(pieces[i].v); } // 1. Sort pieces by Color C sort(pieces.begin() + 1, pieces.end(), [](const Piece& a, const Piece& b) { return a.c < b.c; }); // 2. Coordinate Compression for V values sort(v_coords.begin(), v_coords.end()); v_coords.erase(unique(v_coords.begin(), v_coords.end()), v_coords.end()); // 3. Divide and Conquer // i can range from 1 to N - M + 1 // j can range from M to N // We initialize pointers for seg tree logic cur_L = 1; cur_R = 0; solve(1, N - M + 1, M, N); cout << max_beauty << endl; return 0; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...