結果
問題 |
No.913 木の燃やし方
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:01:36 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 122 ms / 3,000 ms |
コード長 | 9,297 bytes |
コンパイル時間 | 837 ms |
コンパイル使用メモリ | 81,024 KB |
実行使用メモリ | 15,584 KB |
最終ジャッジ日時 | 2025-05-14 13:03:55 |
合計ジャッジ時間 | 7,893 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 34 |
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <algorithm> using namespace std; // Use long long for values that can exceed 32-bit integer limits typedef long long ll; // Use __int128 for intermediate calculations in CHT check to prevent overflow // Products of differences can exceed 64 bits when N is large and |A_i| is large. typedef __int128 int128; // A large value to represent infinity, ensuring it's larger than any possible sadness value. // Max sadness could be around N^2 + N * max(A_i) ~ 4e10 + 2e5 * 1e9 = 4e10 + 2e14. // Min sadness could be negative. Use a large positive value for initial minimums. const ll INF = 4e18; // Structure to represent points for Convex Hull Trick // x coordinate stores 'l', y coordinate stores 'h(l) = l^2 - P_l' struct Point { ll x, y; }; // Function to check if point P2 is redundant for the lower convex hull given P1 and P3. // Returns true if P2 lies on or above the line segment P1P3, meaning P2 should be removed. // Uses cross product with 128-bit integers to avoid overflow and floating point precision issues. // Assumes p1.x < p2.x < p3.x. bool check_redundant(Point p1, Point p2, Point p3) { // Calculate (p2.y - p1.y) * (p3.x - p2.x) and (p3.y - p2.y) * (p2.x - p1.x) int128 val1 = (int128)(p2.y - p1.y) * (p3.x - p2.x); int128 val2 = (int128)(p3.y - p2.y) * (p2.x - p1.x); // If slope(P1, P2) >= slope(P2, P3), P2 is redundant for lower hull. return val1 >= val2; } // Global vector to store points of the lower convex hull for the current subproblem // Reused across recursive calls to potentially save memory allocation time. vector<Point> cht_hull; // Builds the lower convex hull for points[l_idx_start...l_idx_end] from the global set of points. // The global points `global_h_points` contains (l, h(l)) for l=0..N-1. void build_cht(const vector<Point>& global_h_points, int l_idx_start, int l_idx_end) { cht_hull.clear(); // Clear hull from previous usage // Ensure the range is valid if (l_idx_start > l_idx_end) return; // Iterate through the specified range of 'l' indices for (int i = l_idx_start; i <= l_idx_end; ++i) { // Get the point corresponding to index l=i Point p = global_h_points[i]; // Maintain the convex hull property: remove points from the back of the hull // if they become redundant with the new point p. while (cht_hull.size() >= 2 && check_redundant(cht_hull[cht_hull.size() - 2], cht_hull.back(), p)) { cht_hull.pop_back(); } // Add the new point to the hull cht_hull.push_back(p); } } int N; // Number of trees vector<ll> A; // Tree values A[1...N] vector<ll> P; // Prefix sums P[k] = sum_{j=1..k} A_j. P[0] = 0. vector<Point> h_points; // Globally stored points (l, h(l)) = (l, l^2 - P_l) for l=0..N-1 vector<ll> min_sadness; // Stores the result M_i (minimum sadness for tree i) for each i=1..N // Recursive function implementing the Divide and Conquer Optimization strategy. // Computes min_sadness[i] for all i in the range [i_s, i_e]. // The search space for the optimal pair (l, r) is restricted by the ranges [l_s, l_e] and [r_s, r_e]. void solve(int i_s, int i_e, int l_s, int l_e, int r_s, int r_e) { // Base case: if the range of indices 'i' is empty, return. if (i_s > i_e) { return; } // Find the middle index i_m for the current range [i_s, i_e]. int i_m = i_s + (i_e - i_s) / 2; // Determine the effective ranges for l and r specific to computing M_{i_m}. // These ranges must satisfy l < i_m and r >= i_m, and also respect the current bounds [l_s, l_e] and [r_s, r_e]. int current_l_s = l_s; int current_l_e = min(l_e, i_m - 1); // l must be <= i_m - 1 int current_r_s = max(r_s, i_m); // r must be >= i_m int current_r_e = r_e; ll current_min_sadness_for_im = INF; // Initialize minimum sadness for i_m to infinity. int best_l = -1, best_r = -1; // Store the (l, r) pair that achieves the minimum sadness for i_m. // Only proceed with CHT calculation if valid ranges for both l and r exist. if (current_l_s <= current_l_e && current_r_s <= current_r_e) { // Build the Convex Hull Trick structure using points h(l) for l in the effective range [current_l_s, current_l_e]. build_cht(h_points, current_l_s, current_l_e); // Pointer used for optimizing CHT queries since query slopes are monotonic. int CHT_ptr = 0; // Iterate through possible values of r in its effective range. for (int r = current_r_s; r <= current_r_e; ++r) { // If the CHT hull is empty, there are no valid 'l' points for this range. Stop iterating r. if (cht_hull.empty()) break; // The query slope for CHT is -2r. We want to minimize h(l) - 2rl. ll slope = -2LL * r; // Optimize CHT query using the sliding pointer approach. // Since slopes are decreasing as r increases, the optimal point index on the hull is non-decreasing. // Move the pointer `CHT_ptr` right as long as the next point on the hull gives a smaller or equal value. while (CHT_ptr < cht_hull.size() - 1) { ll val1 = cht_hull[CHT_ptr].y + slope * cht_hull[CHT_ptr].x; ll val2 = cht_hull[CHT_ptr+1].y + slope * cht_hull[CHT_ptr+1].x; if (val1 >= val2) { // If next point is better or same, move pointer CHT_ptr++; } else { break; // Current point at CHT_ptr is optimal for this slope } } // Calculate the minimum value of h(l) - 2rl using the optimal point found by CHT pointer. ll query_val = cht_hull[CHT_ptr].y + slope * cht_hull[CHT_ptr].x; // Calculate the total sadness H(l,r) = (h(l) - 2rl) + r^2 + P_r. ll total_sadness = query_val + (ll)r * r + P[r]; // Retrieve the optimal 'l' value (stored in the x coordinate of the CHT point). int current_l = cht_hull[CHT_ptr].x; // Update the minimum sadness found so far for i_m if the current interval [l+1, r] gives a smaller sadness. if (total_sadness < current_min_sadness_for_im) { current_min_sadness_for_im = total_sadness; best_l = current_l; // Store the l that achieved this minimum best_r = r; // Store the r that achieved this minimum } } } // Store the computed minimum sadness for index i_m in the result array. min_sadness[i_m] = current_min_sadness_for_im; // Perform recursive calls for the left subproblem (indices i < i_m) and the right subproblem (indices i > i_m). // Pass potentially tighter bounds for l and r based on the optimal pair (best_l, best_r) found for i_m. // This relies on the monotonicity property: l_i* and r_i* are non-decreasing functions of i. if (best_l == -1) { // If no valid interval was found for i_m (e.g., initial bounds were too restrictive, or sadness remains INF), // it indicates an issue or edge case. To be safe, use the originally passed bounds for recursive calls. // This ensures correctness but might skip potential optimization if bounds could actually be tightened. solve(i_s, i_m - 1, l_s, l_e, r_s, r_e); solve(i_m + 1, i_e, l_s, l_e, r_s, r_e); } else { // Apply monotonicity constraints to restrict search space in recursive calls: // For the left subproblem (i < i_m): optimal l <= best_l, optimal r <= best_r. // Pass updated upper bounds for l and r. solve(i_s, i_m - 1, l_s, min(l_e, best_l), r_s, min(r_e, best_r)); // For the right subproblem (i > i_m): optimal l >= best_l, optimal r >= best_r. // Pass updated lower bounds for l and r. solve(i_m + 1, i_e, max(l_s, best_l), l_e, max(r_s, best_r), r_e); } } int main() { // Use fast I/O operations ios_base::sync_with_stdio(false); cin.tie(NULL); // Read input N, the number of trees cin >> N; // Resize vectors to accommodate N trees (using 1-based indexing for A and P for convenience) A.resize(N + 1); P.resize(N + 1); P[0] = 0; // Initialize prefix sum base case // Read tree values and compute prefix sums for (int i = 1; i <= N; ++i) { cin >> A[i]; P[i] = P[i - 1] + A[i]; } // Precompute points (l, h(l)) = (l, l^2 - P_l) for CHT, for l from 0 to N-1. // Note: Index l corresponds to the start of the interval L = l+1. h_points.resize(N); for (int l = 0; l < N; ++l) { h_points[l] = { (ll)l, (ll)l * l - P[l] }; } // Initialize the result vector to store minimum sadness for each tree i (1 to N). min_sadness.resize(N + 1); // Start the divide and conquer process for all indices i from 1 to N. // The initial search ranges cover all possible l (0 to N-1) and r (1 to N). solve(1, N, 0, N - 1, 1, N); // Output the computed minimum sadness for each tree i. for (int i = 1; i <= N; ++i) { cout << min_sadness[i] << "\n"; } return 0; }