Official

D - Snuke Prime Editorial by tatyam


すぬけプライムは毎日加入及び脱退ができるので、その日支払う料金が \(C\) 円を超えそうならサブスクリプションを利用し、超えないなら利用しないようにするのが最適です。
そこで、
\(s[i]=\) ( すぬけプライムを利用しない場合の \(i\) 日目に支払う料金 )
を求めたいです。
\(i\) の範囲が小さければ いもす法 で求めることができますが、今回は \(i\) の範囲が大きいので、なにか工夫する必要があります。
例えば、以下のようにすれば良いです。

\(a_i\) 日目から \(b_i\) 日目まで利用する \(c_i\) 円のサービスを、

  • \(a_i - 1\) 日目と \(a_i\) 日目 の間に料金が \(c_i\) 円上がるイベントが起こる
  • \(b_i\) 日目と \(b_i + 1\) 日目 の間に料金が \(c_i\) 円下がるイベントが起こる

と言い換え、これらのイベントを時間でソートします。
そして、 イベントを前から見ていきながら、今何日目を見ているのか と 現在の料金 の変数を更新していくと、 この問題を \(O(N\log N)\) で解くことができます。

また、\(a_i\)\(b_i\) を座標圧縮し、いもす法を適用できるようにする方法もあります。

回答例 (C++)

#include <bits/stdc++.h>
using namespace std;
using ll = int64_t;

int main(){
    ll N, C;
    cin >> N >> C;
    vector<pair<ll, ll>> event;
    for(ll i = 0; i < N; i++){
        ll a, b, c;
        cin >> a >> b >> c;
        event.emplace_back(a - 1, c);
        event.emplace_back(b, -c);
    }
    sort(event.begin(), event.end());
    ll ans = 0, fee = 0, t = 0;
    for(auto [x, y] : event){
        if(x != t){
            ans += min(C, fee) * (x - t);
            t = x;
        }
        fee += y;
    }
    cout << ans << endl;
}

回答例 (Python)

N, C = map(int, input().split())
event = []
for i in range(N):
    a, b, c = map(int, input().split())
    a -= 1
    event.append((a, c))
    event.append((b, -c))

event.sort()
ans = 0
fee = 0
t = 0
for x, y in event:
    if x != t:
        ans += min(C, fee) * (x - t)
        t = x
    fee += y

print(ans)

posted:
last update: