Codeforces 467(#267 Div2) E. Alex and Complicated Task

問題

n項からなる数列a[i]が与えられる。a[i]の必ずしも連続しない部分列b[i]で、

  • 長さが4m
  • b[4k + 1] = b[4k + 3]
  • b[4k + 2] = b[4k + 4]

が成り立つもののうち、最長のものを一つ出力せよ。

制約条件

n≦5 * 10^5
-10^9≦a[i]≦10^9

方針

求める数列は、a b a b c d c d ...という形。
a[i] = xに対して、a[prev[i] ] = xなるprev[i](同じ数の直前の出現)はすぐ求まる。
(prev[i], i)の範囲に出現するyであって、prev[i]以前の出現が最も右にあるもの(これをright[i]とする)がわかれば、
dp[i] = max{dp[j] | j<right[i]} + 1という風にアップデートしながらDPできる。


このright[i]の効率のよい求め方がわからなかったので、
平方分割+二分探索とかいう変なことをしたら無情にTLEした(悲しい)


単なるsegment treeのRMQを、更新順序を上手く工夫してやるだけでこのyはわかる。
RMQにiを小さい順にイテレートしながら、prev[i]を入れていくのであるが、
query(prev[i] + 1, i)を調べる前に、最後の出現がprev[i]より大きいyを入れないようにしてやればよい。


具体的には、ループがiまで来たときに初めてupdate(i, prev[i])をすればよい。
(i, next[i])に対するyの最右位置を求めるときは、query(i + 1, next[i])とすればよい。


経路復元が非常に面倒だけど頑張る。

ソースコード

int back[MX], dp[MX], right[MX];
inline void update(int x, int y){
	if(dp[x] >= dp[y] + 1) return;
	dp[x] = dp[y] + 1;
	back[x] = y;
}

int main(){
	
	scanf("%d", &n);
	rep(i, n) scanf("%d", a + i), v.pb(a[i]);
	sort(all(v));
	v.erase(unique(all(v)), v.end());
	rep(i, n) a[i] = lower_bound(all(v), a[i]) - v.begin();
	
	pos.resize(v.size());
	memset(back, -1, sizeof(back));
	memset(prev, -1, sizeof(prev));
	memset(right, -1, sizeof(right));
	
	
	SegTree<int> rmq(n);
	
	rep(i, n){
		if(pos[a[i]].size()) prev[i] = pos[a[i]].back();
		pos[a[i]].pb(i);
	}
	rep(i, n){
		const vi &v = pos[a[i]];
		int x = lower_bound(all(v), i) - v.begin();
		if(x + 1 < v.size()){
			right[v[x + 1]] = rmq.query(i + 1, v[x + 1], 0, 0, rmq.n);
			rmq.update(v[x + 1], i);
		}
	}
	
	rep(i, n){
		if(dp[i + 1] < dp[i]) dp[i + 1] = dp[i], back[i + 1] = -1;
		
		if(prev[i] >= 0 && prev[prev[i]] >= 0 && prev[prev[prev[i]]] >= 0)
			update(i + 1, prev[prev[prev[i]]]);
		
		if(right[i] >= 0) update(i + 1, right[i]);
	}
	
	vi ans;
	int mx = -1, mxi;
	rep(i, n + 1) if(back[i] >= 0 && mx < dp[i]) mx = dp[i], mxi = i;
	
	while(mx > 0){
		rep(i, 2) ans.pb(v[a[mxi - 1]]), ans.pb(v[a[back[mxi]]]);
		mx--; mxi = back[mxi];
		while(mxi >= 0 && (back[mxi] < 0 || dp[mxi] != mx)) mxi--;
	}
	reverse(all(ans));
	printf("%d\n", ans.size());
	rep(i, ans.size()) printf("%d%c", ans[i], i == ans.size() - 1 ? '\n' : ' ');
	
	return 0;
}