おかゆの競プロ日記(その他もあるよ)

主に自分の解いた問題を載せていきます。

CSAcademy Round#53 C

CSAのソースコードのリンクが貼れない(わからない)ので久しぶりにブログに...(この記事は以前あったものを色々あって修正し直したものです)

CSAcademy #Round53 - C(Histogram Partition)

N個の色んな数字からなるAという配列とN個の要素すべてが0の配列Bがあるので, Bのある区間の要素全てに1を足す, という操作をできるだけ少ない回数でAを作りなさい, という問題(答えるのはその回数).

これを, BからAにするのではなく, AからBにする. 初期区間[l, r)を[0, n)として, 1. 現在の区間の最小値を取ってくる(複数ある場合は一番左). 2. 最小値を現在の区間から引き, その最小値の位置をkとすると, [l, k)と[k+1, r)に分割し, それぞれについて1を行う. 最小値が0ならばさらにそこで分割する. 3. これをAの要素すべてが0となるまで行う. このときの操作回数が答え.これをセグメントツリーと分割統治法でやる. 最初はStarrySky木でやろうとしたけどすんごいバグらせたのでセグメントツリーに変えた. 想定解はstackを使ってO(N)でした(すごい).

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define fi first
#define se second

using ll = long long;

// first : value, second : id

template <typename T>
class SegmentTree {
private:
  int n;
  T _init;
  vector<T> seg;
 
  int size (int N) {
    int ret;
    for (ret = 1; ret < N; ret <<= 1);
    return ret;
  }
 
  T query(int a, int b, int k, int l, int r) {
    if (r <= a or b <= l) return _init;
    if (a <= l and r <= b) return seg[k];
 
    T vl = query(a, b, k * 2 + 1, l, (l + r) / 2);
    T vr = query(a, b, k * 2 + 2, (l + r) / 2, r);
    return min(vl, vr);
  }
 
public:
  SegmentTree (int N, T i = 0) : n(size(N)), seg(size(N) * 2, i), _init(i) {}
  SegmentTree () {}
 
  void Init(int N, T i) {
      n = size(N);
      seg.resize(size(N) * 2 - 1, i);
      _init = i;
  }
 
  void Update (int k, T val) {
    k += n - 1;
    seg[k] = val;
    while (k > 0) {
      k = (k - 1) / 2;
      // 適宜変更
      seg[k] = min(seg[k * 2 + 1], seg[k * 2 + 2]);
    }
  }
 
  // [l, r) 開区間であることに注意!!! ... [l, r]ならfind(l, r + 1)
  T Find(int l, int r) {
    return query(l, r, 0, 0, n);
  }
};
int n, a;
SegmentTree<pair<int, int> > segtree;

ll solve(int l, int r, int d);

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

    cin >> n;
    segtree.Init(n, {int(1e9), int(1e9)});
    
    for (int i = 0; i < n; ++i) {
        cin >> a;
        segtree.Update(i, {a, i});
    }

    int l = 0, r = n;

    ll ans = solve(l, r, 0);
    
    cout << ans << endl;
    
    return 0;
}


ll solve(int l, int r, int d) {
    if (l >= r) return 0;
    
    ll ret = 0;

    pair<int, int> p = segtree.Find(l, r);

    if (!(l <= p.se and p.se < r)) return 0;

    if (p.fi - d == 0) {
        ret += solve(l, p.se, d);
        ret += solve(p.se + 1, r, d);
    }
    else {
        ret += 1;
        ret += solve(l, p.se, p.fi);
        ret += solve(p.se + 1, r, p.fi);
    }

    return ret;
}