Loading

【题解】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;
}
posted @ 2021-10-03 17:35  7KByte  阅读(69)  评论(0编辑  收藏  举报