公式

E - 図書館の蔵書点検 / Library Inventory Check 解説 by physics0523


この問題は、イベントソートと segment tree を用いることで解くことができます。

まず、「修理の完了」と「貸し出し計画」を統一的に「イベント」として扱い、時系列順に並べます。但し、同じ日に来る修理の完了と貸し出し計画は、修理の完了が先になるように並べておきます。

次に、全てのイベントについて以下を繰り返します。

  • 区間和を求める segment tree を用意する。これを \(seg\) と呼ぶ。
  • 「修理の完了」が来た場合、 \(seg\) に修理が完了した本の情報を追加する。
  • 「貸し出し計画」が来た場合、現時点での \(seg\) がその時点で修理が完了している本の情報を反映している。よって、この時点での \(seg\) の区間和を適切に調べることで答えが分かる。

時間計算量は \(O((N+Q) \log (N+Q))\) です。

実装例 (C++):

#include<bits/stdc++.h>
#include<atcoder/segtree>

using namespace std;
using namespace atcoder;
using ll=long long;

ll op(ll a,ll b){return (a+b);}
ll e(){return 0;}

typedef struct{
  ll day;
  ll type;
  ll x;
  ll y;
}query;

bool comp(const query &l,const query &r){
  if(l.day==r.day){
    return (l.type<r.type);
  }
  return (l.day<r.day);
}

int main(){
  ll N,Q;
  cin >> N >> Q;
  vector<query> vq;
  for(ll i=0;i<N;i++){
    ll A,D,V;
    cin >> A >> D >> V;
    vq.push_back({D,-1,i,A*V});
  }
  for(ll i=0;i<Q;i++){
    ll L,R,T;
    cin >> L >> R >> T;
    vq.push_back({T,i,L-1,R});
  }
  sort(vq.begin(),vq.end(),comp);

  segtree<ll,op,e> seg(N);
  vector<ll> res(Q);
  for(auto &nx : vq){
    if(nx.type==-1){
      seg.set(nx.x,nx.y);
    }
    else{
      res[nx.type]=seg.prod(nx.x,nx.y);
    }
  }
  for(auto &nx : res){
    cout << nx << "\n";
  }
  return 0;
}

投稿日時:
最終更新: