// 基本テンプレート #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define rep(i,a,n) for(int (i)=(a); (i)<(n); (i)++) #define repq(i,a,n) for(int (i)=(a); (i)<=(n); (i)++) #define repr(i,a,n) for(int (i)=(a); (i)>=(n); (i)--) #define debug(...) fprintf(stderr, __VA_ARGS__) #define int long long int template void chmax(T &a, T b) {a = max(a, b);} template void chmin(T &a, T b) {a = min(a, b);} template void chadd(T &a, T b) {a = a + b;} typedef pair pii; typedef long long ll; int dx[] = {0, 0, 1, -1}; int dy[] = {1, -1, 0, 0}; const ll INF = 1001001001001001LL; const ll MOD = 1000000007LL; // Convex Hull Trick (Verified: COLOCON 2018 Final C) // ・直線集合に直線を追加する // ・直線集合に含まれている関数の中で、 f(x) の最大値を求める // 直線を表現する型と、取得クエリの単位元 (最大値を返すので、大きい負の値とか) template struct ConvexHullTrick { private: using Response = pair; struct Line { Type a, b; Line (Type a_ = 0, Type b_ = 0) : a(a_), b(b_) {} Type get(Type x) { return a*x + b; } }; struct Node { Line line; Node *lhs, *rhs; int index; Node(Line line_, int index_=-1) : line(line_), lhs(nullptr), rhs(nullptr), index(index_) {} ~Node() { if(lhs) delete lhs; if(rhs) delete rhs; } }; int N; vector pos; Node *root; public: // x の取りうる値を sort かつ unique にしたもの ConvexHullTrick(const vector &pos_) : N(pos_.size()), pos(pos_), root(nullptr) {} ~ConvexHullTrick() { if(root) delete root; } // 直線 f(x) = a*x + b を追加する (オプション: インデックスの情報も保持したいならする) void insert(Type a, Type b, int idx=-1) { Line line(a, b); root = update(root, 0, N, line, idx); } // 直線集合 F において、f(x) の最大値を返す Type get_value(Type x) const { int t = lower_bound(pos.begin(), pos.end(), x) - pos.begin(); assert(t < N && pos[t] == x); return query(root, 0, N, t).first; } // 直線集合 F において、f(x) の最大値を実現する直線のインデックスを返す // (複数ある場合はインデックスが最も小さいものを返す) int get_index(Type x) const { int t = lower_bound(pos.begin(), pos.end(), x) - pos.begin(); assert(t < N && pos[t] == x); return query(root, 0, N, t).second; } private: // クエリで処理する区間は閉区間なので注意!!! Node* update(Node* p, int lb, int ub, Line& l, int idx=-1) { if(!p) return new Node(l, idx); if(p -> line.get(pos[lb ]) >= l.get(pos[lb ]) && p -> line.get(pos[ub - 1]) >= l.get(pos[ub - 1])) { return p; } if(p -> line.get(pos[lb ]) <= l.get(pos[lb ]) && p -> line.get(pos[ub - 1]) <= l.get(pos[ub - 1])) { p -> line = l; p -> index = idx; return p; } int mid = (ub + lb) / 2; if(p -> line.get(pos[mid]) < l.get(pos[mid])) { swap(p -> line, l); swap(p -> index, idx); } if(p -> line.get(pos[lb]) <= l.get(pos[lb])) { p -> lhs = update(p -> lhs, lb, mid, l, idx); } else { p -> rhs = update(p -> rhs, mid, ub, l, idx); } return p; } Response comp(Response lhs, Response rhs) const { if(lhs.first != rhs.first) { return lhs.first > rhs.first ? lhs : rhs; } else { return lhs.second < rhs.second ? lhs : rhs; } } Response query(Node *p, int lb, int ub, int t) const { if(!p) return make_pair(id, -1); if(ub - lb == 1) return make_pair(p -> line.get(pos[t]), p -> index); int mid = (ub + lb) / 2; Response cur = make_pair(p -> line.get(pos[t]), p -> index); if(t < mid) { return comp(cur, query(p -> lhs, lb, mid, t)); } else { return comp(cur, query(p -> rhs, mid, ub, t)); } } }; /* // 使用例 int main() { ll N; scanf("%lld", &N); vector points(N); iota(points.begin(), points.end(), 1); vector X(N+1), Y(N+1); ConvexHullTrick cht(points); for(ll j=1; j<=N; j++) { ll A; scanf("%lld", &A); ll x = 2*j, y = -(A + j*j); X[j] = -2*j; Y[j] = A + j*j; cht.insert(x, y, j); } for(auto p : points) { int idx = cht.get_index(p); printf("%lld\n", X[idx]*p + Y[idx] + p*p); } return 0; } */ const int S = 100010; signed main() { int N; cin >> N; vector A(N+1), X(N+1), Y(N+1); for(int i=0; i> A[i+1]; for(int i=0; i> X[i+1]; for(int i=0; i> Y[i+1]; vector points(S); iota(points.begin(), points.end(), 0); ConvexHullTrick cht(points); vector dp(N+1, INF); dp[0] = 0; for(int i=1; i<=N; i++) { if(i != 1) { int val = -cht.get_value(A[i]); dp[i] = val + A[i] * A[i]; } chmin(dp[i], dp[i-1] + (X[i] - A[i]) * (X[i] - A[i]) + Y[i] * Y[i]); int x = -2 * X[i], y = dp[i-1] + X[i]*X[i] + Y[i]*Y[i]; cht.insert(-x, -y); } cout << dp[N] << endl; return 0; }