#include #include #include #include #define rep(i,a,b) for(int i=(a);i<(b);i++) #define rrep(i,a,b) for(int i=(b)-1;i>=(a);i--) #define all(x) begin(x),end(x) #define sz(x) (int)(x).size() using namespace std; using namespace atcoder; using ll=long long; using ull=unsigned long long; using ld=long double; using vll=vector; using vvll=vector; using vi=vector; using vvi=vector; using pll=pair; using pi=pair; // using mint=modint998244353; using mint=modint1000000007; // using mint2=static_modint<2>; // using BoostPoint=boost::polygon::point_data; // using Voronoi=boost::polygon::voronoi_diagram; // using mint=LazyMontgomeryModInt<998244353>; using vm=vector; // using vvm=vector; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); /** * O(N) * * 1. 目的関数の言い換え * A_0=A_{N+1}=0 とし、V(A)=\sum_{i=0}^N |A_i-A_{i+1}| とおく。 * * 各高さ h>=1 について、S_h = {i : A_i>=h} を考え * S_h の極大な連続区間を「高さ h の成分」と呼ぶ 、例えば * A = (1,6,9,2,3,1) の時 h = 3 の成分は [2,3] と [5,5] です * 成分の幅とは、その区間に含まれる添字の個数である。 * * 非負整数 x,y に対して * * |x-y| = \sum_{h>=1} |[x>=h]-[y>=h]| * * が成り立つ * * よって * * V(A) = \sum_{h>=1} \sum_{i=0}^N |[A_i>=h]-[A_{i+1}>=h]| * * 各高さ h で隣接する列の境界を数えると、 * どの成分も左右にちょうど 2 つの境界を持つため、 * * V(A)=2 * (全ての高さにわたる成分の総数) * * となる。したがって、成分の個数を最小化すれば良い。 * * 2. 元の成分をコスト付きアイテムとして扱う * * 元の配列 A の成分の総数を M とし、その幅を重複込みで * * w_1<=w_2<=...<=w_M * * と並べる。 * * 補題: * * V(A) を少なくとも 2q 減らすために必要な操作回数の最小値は * * w_1+w_2+...+w_q * * である。 * * > 下界 * * 最終的な配列 B は 0<=B_i<=A_i としてよい。 * (負の値は 0 に置き換えても操作回数・目的関数は増えない) * * 元の各成分は、操作後に完全に消滅するか、 * 少なくとも 1 つの成分として残る。 * 減算だけでは異なる元の成分が結合することはない。 * * よって、成分数を q 個以上減らすには、 * 元の成分を少なくとも q 個完全に消す必要がある。 * * 幅 w の成分を完全に消すには、その w 個のマスを全て削る必要がある * また、異なる成分のマスは重複しない * したがって、必要な操作回数は少なくとも \sum_{j=1}^q w_j である。 * * > 上界(達成可能) * * 元の成分を「幅の昇順、同じ幅なら高さの降順」に並べ、 * 先頭 q 個の成分を全て削ることを考える * * ある成分のマスの上にあるマスは、必ずその成分に包含されるより高い位置の成分に属する。 * その成分の幅は元の成分以下なので、上の順序では必ず先に選ばれる。 * * つまり、選んだマスは各列の上から連続しており、実際に減算で削れる。 * さらに、成分を丸ごと削るので、残った成分が分裂することもない。 * * よって、ちょうど \sum_{j=1}^q w_j 回の操作で q 個の成分を消せる。 * * 以上より、 * * q=max{t : \sum_{j=1}^t w_j <= K} * 答え=2(M-q) * * 3. 成分の幅ごとの個数を求める * * cnt[w] を「元の配列における幅 w の成分数」と定義する * 幅は 1<=w<=N なので、cnt が求まれば w=1,2,...,N の順に * 予算 K で購入できる成分を貪欲に選べば良い。 * * なお、A_i が大きくても cnt が O(N) で求める。 * * > 成分と最左最小位置 j が対応する * * 高さ h の成分 [L,R] に対して、区間 [L,R] 内で * A_j が最小となる添字のうち、最も左のものを j とする。 * この成分を j に対応させる。 * * 各 j (A_j>0) について * * p_j = max{ij : A_i>N>>K; vll A(N+2),cnt(N+1); rep(i,1,N+1)cin>>A[i]; vi stk={0}; rep(i,1,N+2){ while(A[stk.back()]>A[i]){ int j=stk.back(); stk.pop_back(); int l=stk.back(); cnt[i-l-1]+=A[j]-max(A[l],A[i]); } stk.push_back(i); } ll ans=0; rep(w,1,N+1){ ll t=min(cnt[w],K/w); K-=t*w; ans+=2*(cnt[w]-t); } cout<