Codeforces #156 Div1 A (256 A) Almost Arithmetical Progression

問題

a1 = p
ai+1 = ai * (-1)^i + q
(ただし、p, qは整数)を満たす数列を、ほぼ等差数列と呼ぶ。


与えられた数列b[i]の必ずしも連続しない部分列で、ほぼ等差数列になっているもののうち、最大の長さをもつものの長さを求めよ。

制約条件

n≦4000
0≦b[i]≦10^6

方針

ほぼ等差数列は、x y x y x...と、二つの値を繰り返し取る数列になる。
(x = yの場合もある)


なので、x, yの値を決めうちして、それがどれだけの長さを持てるかを調べればよい。
これは一見O(n^3)の計算量がかかるように見えるが、

  • next[i][j]に、i番目の項の次に値jが出てくるのはいつかという配列を前計算しておく
  • 同じ値の組に対しては、一度しか計算しない

という工夫をすればO(n^2)で計算できる。

ソースコード

int n, a[4000];
int next[4010][4000];

int main(){
	vi vs;
	cin >> n;
	rep(i, n) cin >> a[i], vs.pb(a[i]);
	sort(all(vs));
	vs.erase(unique(all(vs)), vs.end());
	
	memset(next, -1, sizeof(next));
	for(int i = n - 1; i >= 0; i--) rep(j, vs.size()){
		next[i][j] = next[i + 1][j];
		if(a[i] == vs[j]) next[i][j] = i;
	}
	
	int ans = 1;
	rep(q, vs.size()){
		vi v;
		rep(i, n) if(a[i] == vs[q]) v.pb(i);
		rep(r, vs.size()){
			int cnt = 1;
			int cur = v[0] + 1;
			while(cur < n){
				cur = next[cur][r];
				
				if(cur < 0) break;
				cnt++;
				cur++;
				
				if(cur >= n) break;
				cur = next[cur][q];
				
				if(cur < 0) break;
				cnt++;
				cur++;
			}
			ans = max(ans, cnt);
		}
	}
	cout << ans << endl;
	return 0;
}