【题解】ABC221 G - Jumping sequence
给定一个长度为 \(N\) 的序列 \(D\),每次可以选择向一个方向走长度为 \(D\),问是否能走到 \((A,B)\)。
很久没有做到这种让人眼前一亮的题目。
首先我们考虑如果只能走正方向,那么就是将这堆数分称两堆,使得两堆数之和分别为 \(A,B\)。
对于这个简化的问题,目前最优的方法是 bitset 优化背包,所以这题的时间和空间复杂度都不优于 \(\mathcal{O}(\dfrac{N|A|}{64})\)。
再考虑另外一个问题,如果只能在 \(X\) 轴上走,那么就是钦定一些数为正,另外的数为负,使得 \(\sum\limits_{i = 1}^n(-1)^{0/1}D_i=A\),这等价于选出一下数使得 \(\sum D_{p_i} = sum -A\),这是个和上面相同的背包问题。
然后我们对这道题进行最关键的变化,对坐标系进行旋转变化,旋转 \(45\)°,再伸缩变化扩大 \(\sqrt{2}\) 倍,那么每次走的向量是 \((\pm D_i,\pm D_i)\)。不难发现 \(X\) 轴上的位移量和 \(Y\) 轴上的位移量之间互不干扰,分开做背包即可。
#define N 2005
#define M 3600006
int n, A, B, d[N], x[N], y[N];
bitset<M>f[N];
int main() {
//int T = read();while(T--)solve();
n = read(), A = read(), B = read();
f[0][0] = 1;int sum = 0;
rp(i, n)sum += d[i] = read(), f[i] = (f[i - 1] << d[i]) | f[i - 1];
int p = (sum + B - A), q = (sum - A - B);
if(p < 0 || q < 0 || (p & 1) || (q & 1)){No;return 0;}
p /= 2, q /= 2; if(p > M - 6 || q > M - 6){No;return 0;}
if(!f[n][p] || !f[n][q]){No;return 0;}
Yes;
int cc = p, dd = q;
pr(i, n){
if(cc >= d[i] && f[i - 1][cc - d[i]])cc -= d[i], x[i] = 0;
else x[i] = 1;
if(dd >= d[i] && f[i - 1][dd - d[i]])dd -= d[i], y[i] = 0;
else y[i] = 1;
}
rp(i, n){
if(x[i]){
if(y[i])putchar('R');
else putchar('D');
}
else {
if(y[i])putchar('U');
else putchar('L');
}
}el;
return 0;
}