F - Sum Sum Max Editorial by physics0523


この解説はユーザー解説です。


この問題に、物理的な解釈を与えてみましょう。

まず、数列 \(B\) は数列 \(A\) の隣接項の差を表します(\(B_i=A_{i+1}-A_i\))。このことは、 \(A\) を「座標」を表す列であるとすると、 \(B\) は「速度」を表す列であると言い換えられます。
同様に、数列 \(C\) は数列 \(B\) の隣接項の差を表します(\(C_i=B_{i+1}-B_i\))。このことから、 \(B\) が「速度」を表す列であるので、 \(C\) は「加速度」を表す列であると言い換えられます。

では、解法に入ります。
求めるべきは「座標」が最大となる点です。「座標」が最大となる点の候補として、以下を検討すれば十分です。

  • 全ての整数 \(i (1 \le i \le N)\) に対し、 \(A_{y_1+y_2+\dots+y_{i-1}+1}\)
  • 全ての整数 \(i (1 \le i \le N)\) に対し、 \(A_{y_1+y_2+\dots+y_i}\)
  • 区間 \((A_{y_1+y_2+\dots+y_{i-1}+1},A_{y_1+y_2+\dots+y_{i-1}+2},\dots,A_{y_1+y_2+\dots+y_i})\) の内部で、「速度」が \(0\) 以上から \(0\) 以下に切り替わる点。

このうち上 \(2\) つは順次計算を進めることにより \(O(N)\) で計算できます。
では、一番下のものはどのように求めればよいでしょうか?
全ての整数 \(i (1 \le i \le N)\) に対し、数列 \(A\) の区間 \((A_{y_1+y_2+\dots+y_{i-1}+1},A_{y_1+y_2+\dots+y_{i-1}+2},\dots,A_{y_1+y_2+\dots+y_i})\) は「加速度が一定」となります。なので、「その区間の先頭での速度」を「その区間における加速度」を割って \(-1\) を掛けることによって、速度が \(0\) 以上から \(0\) 以下に切り替わる点がどこになるかを求めることができます。
この解法の計算量は \(O(N)\) です。文章では雰囲気だけを説明したので、詳しくは実装例を参照してください。

実装例(C++):

#include<bits/stdc++.h>
 
using namespace std;
 
long long sum(long long l,long long r,long long cnt){
  long long res=(l+r);
  res*=cnt;
  return res/2;
}
 
int main(){
  int t;
  cin >> t;
  for(int cs=0;cs<t;cs++){
    long long n,m;;
    cin >> n >> m;
    vector<long long> a(n),y(n);
    for(int i=0;i<n;i++){cin >> a[i] >> y[i];}

    long long cv=0,cx=0,res=-8e18;
    for(int i=0;i<n;i++){
      long long vl=cv+a[i];
      long long vr=cv+a[i]*y[i];
      if(a[i]!=0){
        if(vl>=0 && vr<=0){
          long long pt=(-cv)/a[i];
          res=max(res,cx+sum(vl,cv+a[i]*pt,pt));
        }
      }
      cv=vr;
      res=max(res,cx+vl);
      cx+=sum(vl,vr,y[i]);
      res=max(res,cx);
    }
    cout << res << '\n';
  }
}

posted:
last update: