AtCoder BC 095 D: Static Sushi
問題
https://beta.atcoder.jp/contests/abc095/tasks/arc096_b
解法
寿司の数が105なので、O(N)であれば間にいそう。ということで、まずは全探索で考えてみる。
考えられるパターンは以下の4ケースに分けられる。
ケース1:時計回りに何個か寿司を食べてそのまま終了する ケース2:時計回りに何個か寿司を食べてからスタート位置に戻り、反時計回りに何個か寿司を食べる ケース3:反時計回りに何個か寿司を食べてそのまま終了する ケース4:反時計回りに何個か寿司を食べてからスタート位置に戻り、時計回りに何個か寿司を食べる
ケース3と4はケース1と2を逆方向にしただけなので、以下ではケース1と2を考える。
ケース1は最大でN個まで寿司を食べる組み合わせしかないのでO(N)で探索可能。
ケース2については、最初に時計回りにi番目まで食べるとすると、スタート位置に戻ってから残ったN-i個の寿司のどこまで食べるかでN-i通り考えられる。
これをそのまま計算するとO(N2)となってしまい部分点しかもらえない。
そこで、事前に残っている寿司がk個の場合にどこまで食べるると最大になるかをkに対して求めておく。 そうすることで、スタート地点に戻ってからの計算量をO(1)に下げることが出来るので、全体としてO(N)に収めることが出来る。
なお、寿司のカロリーについては最初に累積和を求めておき、部分和をO(1)で算出できるようにしておく。
実装
ケース3と4はケース1と2と同じ事を逆方向に実装するだけだが、vとxのベクトルを逆方向にする(vは逆順に並べ換え、xはCから引いてから逆順に並べかえ)ことでケース1と2のロジックを流用できるかなと思ったが、素直にそのまま実装してしまった。
#include<iostream> #include<vector> #include<map> #include<algorithm> #include<cmath> #define FOR(i,a,b) for(int i=(a);i<(b);++i) #define REP(i,n) FOR(i,0,n) using namespace std; typedef long long int ll; const ll INF=(ll)1e18; int main(){ int N; ll C; cin >> N >> C; vector<ll> x(N+1), v(N+1), vc(N+1); x[0] = 0; v[0] = 0; FOR(i,1,N+1)cin >> x[i] >> v[i]; //vの累積和を求める vc[0] = v[0]; FOR(i,1,N+1){ vc[i] = vc[i-1] + v[i]; } vector<ll> r(N+1); r[0] = 0; for(int i = 1; i <= N; i++){ r[i] = max(r[i-1], vc[i]-x[i]); } vector<ll> l(N+1); l[N] = vc[N] - vc[N-1] - (C-x[N]); for(int i = N-1; i >= 1; i--){ l[i] = max(l[i+1], vc[N]-vc[i-1]-(C-x[i])); } ll ans = 0; REP(i,N+1){ //ケース1 ll tmp = vc[i] - x[i]; if(tmp > ans)ans = tmp ; //ケース2 if(i < N){ tmp = vc[i] - 2 * x[i] + l[i+1]; if(tmp > ans)ans = tmp ; } } for(int i = N; i >= 1; i--){ //ケース3 ll tmp = vc[N] - vc[i-1] - (C - x[i]); if(tmp > ans)ans = tmp ; //ケース4 tmp = vc[N] - vc[i-1] - 2 * (C - x[i]) + r[i-1]; if(tmp > ans)ans = tmp ; } cout << ans << endl; }