#include using namespace std; using i64 = long long; using i128 = __int128_t; static const i128 INF128 = (i128(1) << 120); struct Line { i64 slope = 0; i128 intercept = INF128; i128 eval(i64 x) const { return (i128)slope * x + intercept; } }; // 直線追加を rollback できる Li Chao Tree struct RollbackLiChao { int x_left; int x_right; const vector* lines; // 各ノードに格納されている直線番号 vector tree; // (変更したノード, 変更前の直線番号) vector> history; RollbackLiChao( int x_left_, int x_right_, const vector* lines_, int reserve_lines = 0 ) : x_left(x_left_), x_right(x_right_), lines(lines_), tree(4 * max(1, x_right_ - x_left_ + 1) + 16, -1) { if (reserve_lines > 0) { history.reserve((size_t)reserve_lines * 20); } } size_t snapshot() const { return history.size(); } void rollback(size_t snapshot_size) { while (history.size() > snapshot_size) { auto [node, old_line] = history.back(); history.pop_back(); tree[node] = old_line; } } void assign_node(int node, int line_id) { history.push_back({node, tree[node]}); tree[node] = line_id; } void add_line_rec(int node, int left, int right, int line_id) { if (tree[node] == -1) { assign_node(node, line_id); return; } int middle = (left + right) / 2; int current_id = tree[node]; bool better_at_left = (*lines)[line_id].eval(left) < (*lines)[current_id].eval(left); bool better_at_middle = (*lines)[line_id].eval(middle) < (*lines)[current_id].eval(middle); if (better_at_middle) { assign_node(node, line_id); line_id = current_id; } if (left == right) { return; } if (better_at_left != better_at_middle) { add_line_rec( node * 2, left, middle, line_id ); } else { add_line_rec( node * 2 + 1, middle + 1, right, line_id ); } } void add_line(int line_id) { add_line_rec( 1, x_left, x_right, line_id ); } i128 query_rec( int node, int left, int right, int x ) const { i128 answer = INF128; if (tree[node] != -1) { answer = (*lines)[tree[node]].eval(x); } if (left == right) { return answer; } int middle = (left + right) / 2; if (x <= middle) { answer = min( answer, query_rec( node * 2, left, middle, x ) ); } else { answer = min( answer, query_rec( node * 2 + 1, middle + 1, right, x ) ); } return answer; } i128 query(int x) const { return query_rec( 1, x_left, x_right, x ); } }; // LIFO の push/pop と直線最小値を管理するスタック struct HullStack { RollbackLiChao hull; vector line_ids; vector snapshots; HullStack( int x_left, int x_right, const vector* lines, int reserve_lines = 0 ) : hull( x_left, x_right, lines, reserve_lines ) { if (reserve_lines > 0) { line_ids.reserve(reserve_lines); snapshots.reserve(reserve_lines); } } bool empty() const { return line_ids.empty(); } void push(int line_id) { snapshots.push_back(hull.snapshot()); line_ids.push_back(line_id); hull.add_line(line_id); } int pop() { int line_id = line_ids.back(); hull.rollback(snapshots.back()); line_ids.pop_back(); snapshots.pop_back(); return line_id; } i128 query(int x) const { if (empty()) { return INF128; } return hull.query(x); } }; // 二つのスタックでキューを作る struct HullQueue { HullStack input_stack; HullStack output_stack; HullQueue( int x_left, int x_right, const vector* lines, int reserve_lines = 0 ) : input_stack( x_left, x_right, lines, reserve_lines ), output_stack( x_left, x_right, lines, reserve_lines ) {} void push_back(int line_id) { input_stack.push(line_id); } void move_to_output() { if (!output_stack.empty()) { return; } while (!input_stack.empty()) { output_stack.push(input_stack.pop()); } } void pop_front() { move_to_output(); output_stack.pop(); } i128 query(int x) const { return min( input_stack.query(x), output_stack.query(x) ); } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int L; cin >> L; vector capacity(L); for (i64& value : capacity) { cin >> value; } i64 N; cin >> N; const i64 full_cycles = N / L; const int remainder = (int)(N % L); const i64 minimum_capacity = *min_element( capacity.begin(), capacity.end() ); vector reduced_capacity(L); for (int i = 0; i < L; ++i) { reduced_capacity[i] = capacity[i] - minimum_capacity; } vector answer(L, 0); const i64 total_reduced_water = reduced_capacity[0]; // 一周も行わない場合、または残りの水が存在しない場合 if (full_cycles == 0 || total_reduced_water == 0) { answer[0] = total_reduced_water; } else { // 最初の容量 0 の容器 // 関係する容器は 0,...,M-1 int M = 1; while ( M < L && reduced_capacity[M] > 0 ) { ++M; } if (M == 1) { // 直後が容量 0 なので残りの水は動かない answer[0] = total_reduced_water; } else { vector prefix_sum(M + 1, 0); for (int i = 0; i < M; ++i) { prefix_sum[i + 1] = prefix_sum[i] + reduced_capacity[i]; } // r=1,...,M-2 に対応する直線 vector lines(M); for (int r = 1; r <= M - 2; ++r) { lines[r].slope = reduced_capacity[r]; lines[r].intercept = prefix_sum[r] + ( (i128)full_cycles - r ) * reduced_capacity[r]; } const int max_x = max(1, M - 2); HullQueue queue( 1, max_x, &lines, M ); // l=1 のとき有効な r は // 1,...,min(M-2,K) i64 initial_right = min( M - 2, full_cycles ); for ( int r = 1; r <= initial_right; ++r ) { queue.push_back(r); } // F[i] = 辺 i -> i+1 の累積移動量 vector cumulative_flow(M - 1, 0); // W による上限 i128 prefix_minimum = total_reduced_water; for (int l = 1; l <= M - 1; ++l) { i128 best_cost = INF128; // 終点 M-1 に到達しない候補 if (l <= M - 2) { best_cost = queue.query(l) - prefix_sum[l]; } // 終点 M-1 へ直接到達する候補 if ( (i64)M - l <= full_cycles ) { best_cost = min( best_cost, prefix_sum[M] - prefix_sum[l] ); } prefix_minimum = min( prefix_minimum, best_cost ); // prefix_minimum <= W <= 1e9 cumulative_flow[l - 1] = (i64)prefix_minimum; if (l <= M - 2) { // 次の l では r=l が範囲外になる queue.pop_front(); // 新しく r=l+K が範囲に入る i64 new_r = (i64)l + full_cycles; if (new_r <= M - 2) { queue.push_back( (int)new_r ); } } } // 累積移動量から各容器の水量を復元 answer[0] = total_reduced_water - cumulative_flow[0]; for (int i = 1; i <= M - 2; ++i) { answer[i] = cumulative_flow[i - 1] - cumulative_flow[i]; } answer[M - 1] = cumulative_flow[M - 2]; } } // 共通部分 m は完全な周の境界では容器 0 にある answer[0] += minimum_capacity; // 残り N mod L 回を直接シミュレーション for (int i = 0; i < remainder; ++i) { i64 moved = min( answer[i], capacity[i + 1] - answer[i + 1] ); answer[i] -= moved; answer[i + 1] += moved; } for (int i = 0; i < L; ++i) { if (i != 0) { cout << ' '; } cout << answer[i]; } cout << '\n'; return 0; }