Official

G - Jumping sequence Editorial by mechanicalpenciI


まず、\(45\) 度回転させて、\((X+Y, X-Y)\) の座標系において考えます。 この時各方向へのジャンプは \((\pm D_i, \pm D_i)\) (複合任意) と書かれます。
よって、条件は、\(s_i\) および \(s'_i\) にうまく\(+1\)\(-1\) を割り当てることで、

\[ \displaystyle\sum_{i=1}^Ns_iD_i=A+B\quad と \quad \displaystyle\sum_{i=1}^Ns'_iD_i=A-B \]

を同時に達成できるかという問題に帰着できます。
さらに、\(S=\displaystyle\sum_{i=1}^ND_i\) とおき、 式変形する事で、\(t_i\) および \(t'_i\) にうまく\(0\)\(1\) を割り当てることで、

\[ \displaystyle\sum_{i=1}^Nt_iD_i=\frac{S+A+B}{2}\quad と \quad \displaystyle\sum_{i=1}^Nt'_iD_i=\frac{S+A-B}{2} \]

を達成できるかという問題になります。 右辺が負または \(S\) より大きかったり、整数にならない場合は明らかに達成不可能です。
そうでない場合、これは \(dp[x][y]=(\displaystyle\sum_{i=1}^xt_iD_i=y とできるならば 1 , できないならば 0 ) \)としたDPによって解くことができます。 ただし、\(NS\) 回程度の計算を行う必要があるため、適切にbitset高速化や \(64\) 倍高速化を用いる必要があります。 また、操作列の構成はDPの復元として行う事が出来ますが、愚直に行おうとすると、その過程のビット列をすべて持っておく必要があります。 しかし、そのために必要な空間計算量は \(NS\leq 2000\times (1800\times 2000)=7.2\times 10^9\) bit \((=900MB)\) となるため、制約の範囲内に収まっており、この計算を行う事が出来ます。

よって、この問題を解く事が出来ました。 実装例はこの方針でbitset高速化を行ったものです。

また、ちなみに次の方法等で空間計算量を\(5\)MB程度以下に抑えることができます。 復元が無ければ過程のビット列を持っておく必要はなく、 空間計算量は \(S\) bit 程度で足りることに注目し、次の問題を考えます。

長さ \(N\) の正整数列(\(D_1\), \(\ldots\) , \(D_N\)) と非負整数 \(M\) が与えられる。 このとき、\(M=A+B\) をみたす非負整数 \(A\) , \(B\) であって、 \(t_1\) , \(\ldots\) , \(t_N\) \(\in \{0,1\}\) をうまく選ぶことで \( A=\displaystyle\sum_{i=1}^{[\frac{N}{2}]}t_iD_i \) , \( B=\displaystyle\sum_{i=[\frac{N}{2}]+1}^{N}t_iD_i \) とできるようなものを \(1\) つ求める。

これによって得られた \(A\) ,\(B\) を新しい \(M\) として、列の長さを半分にしながら繰り返しこの問題を解く事で明らかに操作列が構成できます。
このときの空間計算量を考えます。この\(A\) , \(B\) を求めるためには過程は必要ないため、必要なビット列の長さは毎回 \(\displaystyle\sum_{i=1}^{N}D_i\) で済み、これを問題ごとに使い回すことができます。 また、 \(A\) , \(B\) として登場する整数も高々 \(2N\) 個程度のため、空間計算量は大きく削減されます。

時間計算量についても \(1\) 回この問題を解くために必要な計算回数は \([\frac{N}{2}]\cdot \displaystyle\sum_{i=1}^{[\frac{N}{2}]}D_i+(N-[\frac{N}{2}])\cdot \displaystyle\sum_{i=[\frac{N}{2}]+1}^{N}D_i \simeq \frac{N}{2}\cdot \displaystyle\sum_{i=1}^{N}D_i\leq \frac{N^2}{2}\max(D_i)\)であり、全体で長さ\(N\) についての問題を \(1\) 回、長さ\(\frac{N}{2}\) についての問題を \(2\) 回、\(\ldots\) 、長さ\(2\) についての問題を \(2^{[log_2N]}\) 回解くことになるので、 合計の計算回数は\(\max(D_i)\cdot (\frac{N^2}{2}+\frac{N^2}{4}+\cdots )\leq N^2\max(D_i)\) で抑えられ、時間計算量をほぼ犠牲にすることなく、空間計算量を削減する事が出来ました。
(参考: https://atcoder.jp/contests/abc221/submissions/26297191 ,64倍高速化+空間計算量削減, 108ms,4160KB)

c++による実装例 :

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

#define rep(i, n) for(int i = 0; i < n; ++i)
#define rep3(i, a, b) for(int i = a; i >= b; i--)

int main(void) {
	int n;
	int m[2];
  
	bitset<3600001> a[2001];
	int b[2000];
	char c[4] = { 'L','U','D','R' };
  
	bool v = true;
	int x, y;
	string ans;

	cin >> n >> x >> y;
	m[0] = x + y;
	m[1] = x - y;
	x = 0;
	rep(i, n) {
		cin >> b[i];
		x += b[i];
	}
	rep(i, 2)if (abs(m[i]) > abs(x))v = false;
	rep(i, 2)if ((m[i] + x) % 2 != 0)v = false;
	if (!v) {
		cout << "No" << endl;
		return 0;
	}
	rep(i, 2)m[i] = (m[i] + x) / 2;
	a[0][0] = true;
	rep(i, 2000)a[i + 1] = a[i] | (a[i] << b[i]);
	if ((!a[n][m[0]]) || (!a[n][m[1]])) {
		cout << "No" << endl;
		return 0;
	}
	rep3(i, n - 1, 0) {
		x = 0;
		rep(j, 2) {
			if (!a[i][m[j]]) {
				m[j] -= b[i];
				x += (1 << j);
			}
		}
		ans = c[x] + ans;
	}
	cout << "Yes" << endl;
	cout << ans << endl;

	return 0;
}

posted:
last update: