SRM 404: KSubstring

(問題概要)
A[0] .. A[n - 1] (引数から線形合同法みたいなので構成)
に対して、s(i, k) = A[i] + ... + A[i + k - 1] とする。

s(i, k) - s(j, k) (i + k - 1 < j)

を最小にするkとその値を求めよ。

(解法)
kを1ずつ増やしていった時に、s(i, k) (i = 0 .. n-1) の更新はO(n)でできる。
各jに対して目的の値を最小にするiは2分探索で求まる。
全体でO(n ^ 2 log n)。
2分探索をstd::lower_boundでやったらどうやらO(log n)にならないっぽく、std::set::lower_boundを使わなければいけない模様。
本番にはまらなくて良かった・・・。

#include <cstdlib>
#include <algorithm>
#include <cassert>
#include <climits>
typedef long long ll;

#define REP(i,n) for(int i=0; i<(int)(n); i++)

#include <queue>
#include <set>
#include <iostream>
#include <sstream>

using namespace std;

class KSubstring {
public:
  vector <int> maxSubstring(int A0, int X, int Y, int M, int n);
};

vector <int> KSubstring::maxSubstring(int A0, int X, int Y, int M, int n){
  vector<int> a(n);

  REP(i,n){
    a[i] = A0;
    A0 = ((ll)A0 * X + Y) % M;
  }

  vector<ll> s(a.begin(), a.end());

  ll val = 1ll << 60;
  int ans = 0;

  for(int k = 1; k <= n; k++){
    set<ll> memo;

    ll m = 1ll << 60;
    for(int i = k; i + k - 1 < n; i++){
      const ll v = s[i];

      memo.insert(s[i - k]);

      set<ll>::iterator it =
	memo.lower_bound(v);

      if(it != memo.end())
	m = min(m, std::abs(v - *it));
      if(it != memo.begin())
	m = min(m, std::abs(v - *(--it)));

      if(m <= val){
	val = m;
	ans = k;
      }
    }

    REP(j,n) if(j + k < n)
      s[j] += a[j + k];
  }

  vector<int> ret(2);
  ret[0] = ans;
  ret[1] = val;

  return ret;
}