1 条题解

  • 0
    @ 2025-8-13 15:02:39

    原题链接:https://codeforces.com/gym/102803/problem/L

    考虑把数对对应到平面直角坐标系上的坐标,那么四个操作分别对应向右、上、左、下。点的顺序的第一关键字显然是到 (0,0)(0,0)​ 的曼哈顿距离,也就是所在的圈数。对于距离相同的点,其实就是把原点到它的路径写成包含“右上左下”的字符串,然后取字典序最小的一种,不同点的顺序就是这个字符串的字典序比较。

    那么只要是带有“右”就按谁右多排序,不带右的就按谁上多排序,不带右和上的就按谁左多排序。

    把这个规律画在图上大概就是:每一圈右半边从右到左上下震荡,左半边(包括正中间)就是顺时针转过来。就像下图:

    $$\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
    上传者