Cod sursa(job #3350061)

Utilizator mircea_007Mircea Rebengiuc mircea_007 Data 5 aprilie 2026 10:04:51
Problema Euro Scor 55
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 5.3 kb
#include <iostream>
#include <fstream>
#include <cassert>

#include <algorithm>
#include <vector>

#include <random>

template<class T> using vec = std::vector<T>;
using ll = long long;

constexpr ll INF = 1e18;

template<class Extra> struct Treap {
  struct Node {
    int left, right;
    ll pri;
    Extra extra;
  };
  
  std::mt19937 rng;
  vec<Node> t;
  Treap(): rng(69), t(1, {0, 0, 0, Extra()}) {}

  int new_node( const Extra& extra ) {
    t.push_back({ 0, 0, rng(), extra });
    return (int)t.size() - 1;
  }

  int join( int left, int right ) {
    if( !left ) return right;
    if( !right ) return left;

    if( t[left].pri > t[right].pri ){
      t[left].right = join( t[left].right, right );
      return left;
    }else{
      t[right].left = join( left, t[right].left );
      return right;
    }
  }

  template<class CB> std::pair<int, int> split( int root, CB&& is_left ) {
    if( !root ) return { 0, 0 };

    if( is_left( t[root].extra ) ){
      auto [A, B] = split( t[root].right, is_left );
      t[root].right = A;
      return { root, B };
    }else{
      auto [A, B] = split( t[root].left, is_left );
      t[root].left = B;
      return { A, root };
    }
  }

  int insert( int root, const Extra& extra ) {
    auto [A, B] = split( root, [&]( const Extra& that ) -> bool { return that < extra; } );
    return join( A, join( new_node( extra ), B ) );
  }

  int leftmost( int u ) const {
    while( t[u].left ) u = t[u].left;
    return u;
  }

  int rightmost( int u ) const {
    while( t[u].right ) u = t[u].right;
    return u;
  }

  int yeet_leftmost( int u ) {
    if( !t[u].left ) return t[u].right;
    t[u].left = yeet_leftmost( t[u].left );
    return u;
  }

  int yeet_rightmost( int u ) {
    if( !t[u].right ) return t[u].left;
    t[u].right = yeet_rightmost( t[u].right );
    return u;
  }
};

struct Pisat {
  ll begin, ct, slope;
  ll operator ()( ll x ) const { return ct + x * slope; }
  bool operator < ( const Pisat& that ) const {
    return begin < that.begin;
  }
};

struct LineContainer : Treap<Pisat> {
  static constexpr ll WIDTH = 1e4;

  int root;
  LineContainer():
    Treap<Pisat>(),
    root(new_node( Pisat{ -WIDTH, -INF, 0 } )) {}

  void dump() {
    auto dfs = [&]( auto &&self, int u, int ind = 2 ) -> void {
      if( !u ) return;
      self( self, t[u].left, ind + 2 );
      for( int i = 0; i < ind; i++ )
        std::cerr << ' ';
      std::cerr << "(u = " << u << ") [begin = " << t[u].extra.begin << ", ct =" << t[u].extra.ct << ", slope = " << t[u].extra.slope << "]" << std::endl;
      self( self, t[u].right, ind + 2 );
    };
    dfs( dfs, root );
  }

  void push( ll ct, ll slope ) {
    auto [A, B] = split( root, [&]( const Pisat& that ) -> bool { return that.slope <= slope; } );
    ll split_point = (B ? t[leftmost( B )].extra.begin : +WIDTH);
    bool dreapta = (query( split_point ) < ct + split_point * slope);

    ll begin; {// taie din stanga
      int u;
      while( (u = rightmost( A )) && t[u].extra( t[u].extra.begin ) <= ct + slope * t[u].extra.begin )
        A = yeet_rightmost( A );

      if( !u ) begin = -WIDTH;
      else{
        auto [_, that_ct, that_slope] = t[u].extra;
        // ct + x * slope >= that_ct + x * that_slope
        // x * (slope - that_slope) >= (that_ct - ct) --> ceil
        if( slope > that_slope )
          begin = std::min( split_point, (that_ct - ct - 1) / (slope - that_slope) + 1 );
        else
          begin = split_point;
      }
    }

    if( !dreapta ){
      if( begin < split_point )
        A = join( A, new_node(Pisat{ begin, ct, slope }) );
      root = join( A, B );
      return;
    }

    if( B ){// taie din dreapta
      Pisat zile = t[leftmost( B )].extra;
      B = yeet_leftmost( B );
      int u;
      while( (u = leftmost( B )) && zile(t[u].extra.begin - 1) <= ct + (t[u].extra.begin - 1) * slope ){
        zile = t[u].extra;
        B = yeet_leftmost( B );
      }

      ll end = (ct - zile.ct - 1) / (zile.slope - slope) + 1;
      B = join( new_node(Pisat{ end, zile.ct, zile.slope }), B );
    }

    root = join( join( A, new_node(Pisat{ begin, ct, slope }) ), B );
  }
  
  ll query( ll x ) {
    auto [A, B] = split( root, [&]( const Pisat& that ) -> bool { return that.begin <= x; } );
    ll ret = t[rightmost( A )].extra( x );
    root = join( A, B );
    return ret;
  }
};

struct Brut {
  vec<std::pair<ll, ll>> lines;
  void push( ll ct, ll slope ) { lines.emplace_back( ct, slope ); }
  ll query( ll x ) {
    ll ret = -INF;
    for( const auto &[ct, slope]: lines )
      ret = std::max( ret, ct + x * slope );
    return ret;
  }
};

int main() {
  std::ifstream fin("euro.in");
  std::ofstream fout("euro.out");

  int n, fee;
  fin >> n >> fee;

  vec<int> costs(n);
  for( int &x : costs ) fin >> x;
  
  vec<ll> sp(n + 1, 0);
  for( int i = 0; i < n; i++ )
    sp[i + 1] = sp[i] + costs[i];

  // dp[i] = -fee + max{ dp[j] + (sp[i] - sp[j]) * i }
  // dp[i] = -fee + sp[i] * i + max{ (dp[j]) + i * (-sp[j]) }

  LineContainer zile;
  zile.push( 0, 0 );
  ll ret = 0;
  for( int i = 1; i <= n; i++ ){
    // zile.dump();
    // std::cerr << "zile.query( " << i << ") = " << zile.query( i ) << std::endl;
    // std::cerr << "churneidau.query( " << i << ") = " << churneidau.query( i ) << std::endl;
    
    ret = sp[i] * (ll)i + zile.query( i ) - fee;
    zile.push( ret, -sp[i] );
  }

  fout << ret << std::endl;
  return 0;
}