公式

F - 1122 Subsequence 解説 by mechanicalpenciI


以下では連続とは限らない部分列を単に部分列とよびます。

この問題はbitDP によって解くことができます。
\(1\leq A_i\leq 20\) より、\(A\) の部分列であるような 1122数列の長さとしてあり得る最大値は \(2\times 20=40\) であることに注意してください。

まず、\(\{1,2,\ldots,20\}\) の部分集合 \(S\) (空集合および \(S\) 自身を含む)に対して 、\(dp[S]\) を、\((A_1, A_2,\ldots, A_r)\) が 1122数列であって、 その列に含まれる整数の集合がちょうど \(S\) になるようなものを部分列としてもつような最小の非負整数 \(r\) (ただし、\((A_1, A_2,\ldots, A_N)\) にそのような列が含まれない場合は \(\infty\) )として定義します。
求めたい答えは \(dp[S]\neq \infty\) であるような \(S\) における、\(2\times \lvert S\rvert\) の最大値です。ここで、\(\lvert S\rvert\)\(S\) の要素数を表します。
また、\(0\leq i\leq N\), \(1\leq x\leq 20\) について、\(f(i,x)\)\(i\bm{<}j\leq N\) かつ \(f(j)=x\) をみたす最小の \(j\)(存在しない場合は \(\infty\))と定義します。便宜上、任意の \(1\leq x\leq 20\) について、\(f(\infty,x)=\infty\) とします。

\(dp[\phi]\)\(\phi\) は空集合を表します)はつねに \(r=0\) となります。
さて、\(S=\{e_1,e_2,\ldots, e_{\lvert S\rvert}\}\) とし、\(S\) 自身を除く \(S\) の部分集合 \(S'\) に対して \(dp[S']\) が求まっているとき、\(S\) に属する整数をすべて含む1122数列の最後の \(2\) 項について場合分けすることで、 \(S\) は次のように計算することができます。
\(1\leq i\leq \lvert S\rvert\) について、集合 \(T_i\)\(S\) から \(e_i\) だけを除いたもの(すなわち、\(T_i\equiv S-\{ e_i\}\))として定義します。 このとき、含まれる整数の集合が \(S\) である1122数列のうち、最後の \(2\) 項が \(e_i\) であるようなものを \((A_1, A_2,\ldots, A_r)\) が含む最小の \(r\) は、\(f(f(dp[T_i],e_i),e_i)\) で求めることができます。よって、\(dp[S]\)\(\displaystyle dp[S]=\min_{1\leq i\leq \lvert S\rvert} f(f(dp[T_i],e_i),e_i)\) として求めることができます。
これにより、\(S\) の要素数について昇順に \(dp[S]\) を求めていくことができます。

計算量について、\(f(i,x)\) は事前に計算してテーブルで用意しておくことで \(O(1)\) で求めることができます。よって、各集合について \(dp[S]\)\(O(\lvert S\rvert)\) で計算することができ、全ての \(S\) について求めるのにかかる計算量は\(M=\max(A_i)\) として、 \(O(M\cdot 2^M)\) となります。 なお、\(f(i,x)\) のテーブルの前計算にかかる計算量は、\(i\) について降順に求めていくことで、 \(O(NM)\) でできることから、全体で \(O(M(N+2^M))\) であり、\(N\leq 2\times 10^5, M\leq 20\) の今回の制約のもとで十分高速です。
よって、この問題を解くことができました。

c++ による実装例:

#include <bits/stdc++.h>
using namespace std;

#define rep(i, n) for(int i = 0; i < n; ++i)
#define N 200000
#define M 20

int main(void){
	int n;
	int a[N];
	int nxt[N+2][M];
	int r[(1<<M)];
	int ans=0,cnt;

	cin>>n;
	rep(i,n)cin>>a[i];

	rep(j,M)nxt[n][j]=n+1,nxt[n+1][j]=n+1;  //n+1=\infty
	for(int i=n-1;i>=0;i--){
		rep(j,M)nxt[i][j]=nxt[i+1][j];
		nxt[i][a[i]-1]=i+1;
	}

	rep(i,(1<<M))r[i]=n+1;
	r[0]=0;
	rep(i,(1<<M)){
		cnt=0;
		rep(j,M){
			if((i>>j)&1){
				cnt+=2;
				r[i]=min(r[i],nxt[nxt[r[i^(1<<j)]][j]][j]);
			}
		}
		if(r[i]<=n)ans=max(ans,cnt);
	}

	cout<<ans<<endl;
	return 0;
}

投稿日時:
最終更新: