1 条题解
-
0
原题链接:https://codeforces.com/gym/102803/problem/L
考虑把数对对应到平面直角坐标系上的坐标,那么四个操作分别对应向右、上、左、下。点的顺序的第一关键字显然是到 的曼哈顿距离,也就是所在的圈数。对于距离相同的点,其实就是把原点到它的路径写成包含“右上左下”的字符串,然后取字典序最小的一种,不同点的顺序就是这个字符串的字典序比较。
那么只要是带有“右”就按谁右多排序,不带右的就按谁上多排序,不带右和上的就按谁左多排序。
把这个规律画在图上大概就是:每一圈右半边从右到左上下震荡,左半边(包括正中间)就是顺时针转过来。就像下图:
$$\def\arraystretch{1.5} \begin{array}{ccccc} & & &18\\ & &19&8&16\\ &20&9&2&6&14\\ 21&10&3&0&1&5&13\\ &22&11&4&7&15\\ &&\dots&12&17\\ &&&\dots \end{array}$$所以从坐标找自然数只需要找到它在第几圈然后分类讨论左右即可。自然数找坐标需要开一下根号,这个注意不要出精度问题。剩下的就是代码实现的细节,记得开 long long。
#include <cstdio> #include <cmath> typedef long long ll; ll abs(ll x) {return x > 0 ? x : -x; } ll x, y, id; void work1() { scanf("%lld%lld", &x, &y); ll c = abs(x) + abs(y); if (c == 0) {puts("0"); return; } ll p = 2*(c-1)*c; if (x > 0) id = p + 2*(c-x) + (y<=0); else id = p + 3*c - y; printf("%lld\n", id); } void work2() { scanf("%lld", &id); if (id == 0) {puts("0 0"); return; } ll c = sqrtl(id/2) + 10; while (2*(c-1)*c >= id) c--; ll p = 2*(c-1)*c; if (id-p < 2*c) { x = c-(id-p)/2; y = c-x; if ((id-p) & 1) y = -y; } else { y = 3*c-(id-p); x = -(c-abs(y)); } printf("%lld %lld\n", x, y); } int main() { int n, m; scanf("%d%d", &n, &m); while (n--) work1(); while (m--) work2(); }
- 1
信息
- ID
- 41
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 24
- 已通过
- 3
- 上传者