#include <vector> #include <cstdint> #include <iostream> #include <algorithm> using namespace std; namespace myrandom { uint64_t seed = 1234567891234567891; uint64_t xorshift64() { seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17; return seed; } int next_int(int l, int r) { return l + int(xorshift64() % (r - l)); } } const int INF = 1012345678; const int D = 100'000'000; int get_error(int a, int b) { int d = abs(a - b); return min(d, D - d); } int get_score(int N, const vector<vector<int> >& A, const vector<int>& ans) { vector<vector<int> > B(N); B[N - 1] = ans; for (int i = N - 2; i >= 0; i--) { B[i].resize(i + 1); for (int j = 0; j <= i; j++) { B[i][j] = (B[i + 1][j] + B[i + 1][j + 1]) % D; } } int score = -INF; for (int i = 0; i < N; i++) { for (int j = 0; j <= i; j++) { int d = abs(B[i][j] - A[i][j]); d = min(d, D - d); score = max(score, d); } } return D / 2 - score; } int get_opt_1(int K, vector<int> A) { sort(A.begin(), A.end()); A.push_back(A[0] + D); pair<int, int> opt = {-INF, -1}; for (int i = 0; i < K; i++) { opt = max(opt, {A[i + 1] - A[i], i}); } return ((A[opt.second] + A[opt.second + 1]) / 2 + D / 2) % D; } int get_opt_2(int K, vector<int> A, vector<int> B) { sort(A.begin(), A.end()); A.push_back(A[0] + D); vector<pair<int, int> > seg; for (int i = 0; i < K; i++) { if (A[i + 1] - A[i] > D / 10) { seg.push_back({A[i] + D / 2, A[i + 1] + D / 2 + 1}); } } if (seg.empty()) { return -1; } int opt_error = D / 2; int opt_x = -1; for (int id = 1; id <= 25; id++) { int u = myrandom::next_int(0, seg.size()); int x = myrandom::next_int(seg[u].first, seg[u].second); x %= D; int error = 0; for (int i = 0; i < K; i++) { error = max(error, get_error(x, A[i])); } vector<int> C(K + 1); for (int i = 0; i < K + 1; i++) { C[i] = (B[i] - 1LL * (K - i) * x % D + D) % D; } sort(C.begin(), C.end()); C.push_back(C[0] + D); int z = 0; for (int i = 0; i < K + 1; i++) { z = max(z, C[i + 1] - C[i]); } error = max(error, D / 2 - z / 2); if (opt_error > error) { int delta = opt_error - error; opt_error = error; opt_x = x; vector<pair<int, int> > nseg; for (auto [l, r] : seg) { if (l + delta < r - delta) { nseg.push_back({l + delta, r - delta}); } } seg = nseg; } } return opt_x; } vector<int> solve(int N, const vector<vector<int> >& A) { const int repeats = 2000; int best_score = 0; vector<int> opt; for (int id = 1; id <= repeats; id++) { vector<vector<int> > B(N); for (int i = 0; i < N; i++) { B[i].resize(i + 1); } int error = D / 2 - best_score; B[N - 1][0] = (A[N - 1][0] + myrandom::next_int(-error / 2, +error / 2 + 1) + D) % D; bool fail = false; for (int i = 1; i < N; i++) { for (int j = 0; j < (i != N - 1 ? 2 : 1); j++) { for (int k = i - 1 + j; k >= 0; k--) { B[(N - i - 1 - j) + k][k] = (B[(N - i - j) + k][k] + B[(N - i - j) + k][k + 1]) % D; } } int center = 0; if (i == N - 1) { vector<int> e1(i + 1); for (int j = 0; j <= i; j++) { e1[j] = (A[(N - i - 1) + j][j] - B[(N - i - 1) + j][j] + D) % D; } center = get_opt_1(i + 1, e1); } else { vector<int> e1(i + 1), e2(i + 2); for (int j = 0; j <= i; j++) { e1[j] = (A[(N - i - 1) + j][j] - B[(N - i - 1) + j][j] + D) % D; } for (int j = 0; j <= i + 1; j++) { e2[j] = (A[(N - i - 2) + j][j] - B[(N - i - 2) + j][j] + D) % D; } center = get_opt_2(i + 1, e1, e2); if (center == -1) { fail = true; } } B[N - 1][i] = center; for (int j = i - 1; j >= 0; j--) { B[(N - i - 1) + j][j] = (B[(N - i) + j][j] + B[(N - i) + j][j + 1]) % D; } } if (!fail) { int score = get_score(N, A, B[N - 1]); if (best_score < score) { cerr << id << ": " << score << endl; best_score = score; opt = B[N - 1]; } } } return opt; } int main() { int N; cin >> N; vector<vector<int> > A(N); for (int i = 0; i < N; i++) { A[i].resize(i + 1); for (int j = 0; j <= i; j++) { cin >> A[i][j]; } } vector<int> ans = solve(N, A); for (int i = 0; i < N; i++) { if (i != 0) { cout << ' '; } cout << ans[i]; } cout << endl; cerr << "score: " << get_score(N, A, ans) << endl; return 0; }