結果

問題 No.913 木の燃やし方
ユーザー qwewe
提出日時 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
権限があれば一括ダウンロードができます

ソースコード

diff #

#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;
}
0