题解 AT4432 【[ARC103B] Robot Arms】

皎月半洒花

2020-03-20 11:41:29

Solution

大概是给出了和cyj不同的另一种证明方法和另一种构造方式?但其实本质是一样的。 _____ 首先无解很容易判断。因为多走一步和少走一步之间的差值总是 $2\times$步长 ,所以如果 $x_i+y_i$ 的奇偶性不同的话,就不能同时满足条件。 发现这个题的本质是拆分问题。那么大概可以引导到「二进制拆分」这种泛式的拆分方式上。不妨考虑对于一组 $\{1,2,4\cdots 2^k\}$ 可以拼出哪些坐标。那么考虑对于一组坐标 $(x,y)$ ,他可以被通达,那么可以知道如果想要减小某一维坐标,比如 $x\to x'$,那有两种情况: 1、只是单纯地拿掉几个元素。比如 $16\to11$ 就是简单地拆出 $4$ 和 $1$ 。 2、拿出几个元素并且换进去几个元素。比如 $12\to11$ 就是拆出 $2$ 和 $1$ 放进一个 $4$ 。 可以看出,这是可以相互通达的。 更详细一点。因为对于四个象限,情况都是等价的。所以只考虑第一象限。发现对于 $\{1,2,4\cdots 2^k\}$ ,它可以维护到的位置至少是所有 $|x+y|=\sum _{i=0}^k 2^i=2^{k+1}-1$。但是不仅限于此。发现如果将将其中某个元素的贡献取负,那么就可以访问到所有 $|x+y| - \sum 2^p$ 的格子。换句话说,这个方式可以访问到所有与 $2^{k+1}-1$ 奇偶性相同的格子。 到这一步就已经很完美了,但是依然存在问题。如果 $|x+y|$ 都是偶数,这就挂了。所以此时可以随便向 $x$ 轴或者 $y$ 轴偏移 $1$ ,比如把 $(1,0)$ 变换成原点,就解决了。 实现方面,发现最合法区域后是个内部镂空的菱形。找出来之后,对于一组坐标 $(x,y)$ ,考虑对称着找。反向从大到小枚举每一个步长,如果新的点的 $|x|+|y|$ 之和严格小于当前的步长,就是可以拼出来的。不难知道这样做是对的。 ```cpp int n ; int cnt ; int _eve ; ll nx, ny ; int bc[N] ; struct rg{ int l ; int r ; int t ; }base[N] ; //D R U L int dx[4] = {0, 1, 0, -1} ; int dy[4] = {-1, 0, 1, 0} ; char s[4] = {'U', 'L', 'D', 'R'} ; int main(){ cin >> n ; bc[0] = 1 ; for (int i = 1 ; i <= n ; ++ i){ cin >> base[i].l >> base[i].r ; base[i].t = abs(base[i].l + base[i].r) & 1 ; } for (int i = 2 ; i <= n ; ++ i) if (base[i].t != base[1].t) return puts("-1"), 0 ; for (int i = 1 ; i <= 30 ; ++ i) bc[++ cnt] = 1 << i ; cout << cnt + 1 + (bool)(!(base[1].t & 1)) << '\n' ; if (!(base[1].t & 1)) cout << 1 << " " ; for (int i = cnt ; ~i ; -- i) printf("%d%c", bc[i], " \n"[!i]) ; for (int i = 1 ; i <= n ; ++ i){ nx = base[i].l ; ny = base[i].r ; if (!(base[i].t & 1)) ny -= 1, putchar('U') ; //cout << nx << " " << ny << endl ; for (int j = cnt ; ~j ; -- j){ for (int k = 0 ; k < 4 ; ++ k){ ll kx = nx + (ll)dx[k] * bc[j] ; ll ky = ny + (ll)dy[k] * bc[j] ; if (abs(kx) + abs(ky) < bc[j]){ nx = kx ; ny = ky ; //cout << nx << " " << ny << endl ; printf("%c", s[k]) ; break ; } } } puts("") ; } return 0 ; } ``` #