ferinの競プロ帳

競プロについてのメモ

SoundHound Programming Contest 2018 Masters Tournament 本戦 B - Neutralize

問題ページ
B - Neutralize

解法

DPをする。dp[i] = (区間[0,i]の最大値) とする。遷移について考えると

  • max(dp[j]) (0<=j<=i-k)
  • dp[i-1] + b[i]
  • 0 (k-1<=i)
    の3通りになる。上から順に区間[j,i]を0にする、i番目の薬について0にせず足す、区間[0,i]全体を0にするを表す。
    ここで一番上のmaxについて愚直に求めると遷移がO(N)かかってTLEだが区間maxなのでセグメント木で高速に求められる。したがって全体でO(NlogN)で解ける。

N<=10^5を見てDPする気持ちになれない、難しすぎる

ソースコード

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
#define int ll
using VI = vector<int>;
using VVI = vector<VI>;
using PII = pair<int, int>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define PB push_back

const ll INF = (1LL<<60);
const int MOD = 1000000007;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }
template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
  out<<'('<<a.first<<','<<a.second<<')';
  return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
  out<<'[';
  REP(i, a.size()) {out<<a[i];if(i!=a.size()-1)out<<',';}
  out<<']';
  return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

template <typename monoid>
class segmentTree {
public:
  using M = monoid;
  using T = typename M::value_type;

  int sz;
  vector<T> x;

  segmentTree(int n = 1e5) {
    sz = 1; while(sz < n) sz *= 2;
    init();
  }
  void init() { x.assign(sz*2, M::id()); }

  // [a, b)
  T query(int a, int b, int k, int l, int r) {
    if(r <= a || b <= l) return M::id();
    if(a <= l && r <= b) return x[k];
    return M::f(query(a, b, 2*k+1, l, (l+r)/2),
                query(a, b, 2*k+2, (l+r)/2, r));
  }
  T query(int a, int b) {return query(a, b, 0, 0, sz);}
  // 点更新
  void update(int i, const T &val) {
    i += sz-1;
    x[i] = M::g(x[i], val);
    while(i > 0) {
      i = (i-1) / 2;
      x[i] = M::f(x[i*2+1], x[i*2+2]);
    }
  }
};

template <typename T>
struct max_monoid {
  using value_type = T;
  static constexpr T id() { return std::numeric_limits<T>::min(); }
  static T f(const T &a, const T &b) { return max(a, b); }
  static T g(const T &a, const T &b) { return b; }
};
template <typename T>
using rmq = segmentTree<max_monoid<T>>;

signed main(void)
{
  cin.tie(0);
  ios::sync_with_stdio(false);

  int n, k;
  cin >> n >> k;
  VI a(n);
  REP(i, n) cin >> a[i];

  if(k == 1) {
    int ret = 0;
    REP(i, n) if(a[i]>0) ret+=a[i];
    cout << ret << endl;
    return 0;
  }

  rmq<int> seg(n);
  VI dp(n+5, -INF);
  dp[0] = a[0];
  seg.update(0, dp[0]);
  FOR(i, 1, n) {
    if(i >= k-1) dp[i] = 0;
    if(i > k) chmax(dp[i], seg.query(0, i-k+1));
    chmax(dp[i], dp[i-1] + a[i]);
    seg.update(i, dp[i]);
  }
  cout << max(0LL, dp[n-1]) << endl;

  return 0;
}