[日常训练] 距离之和

【问题描述】

想象一个机器人位于二维空间。初始时,机器人在(0,0)。有4个命令S,J,I,Z。具体的,如果机器人在(x,y),在收到S命令之后,移动到(x,y+1),收到J之后,移动到(x,y-1),I命令之后移动到(x+1,y),Z命令之后移动到(x-1,y)。在这个二维空间有n个固定的点,在每个命令之后,每个固定点会计算自己与机器人的曼哈顿距离,然后返回这些距离的总和。
PS:两个点(x1,y1)和(x2,y2)的曼哈顿|x1-x2|+|y1-y2|。

【输入格式】
第一行是正整数n和m。
接下来n行,每行两个整数,表示控制点的坐标,有些控制点坐标可能相同。
接下来一行,一个长度为m的字符串,表示机器人依次接受到的命令。
【输出格式】
输出m行,每行一个整数,第i行表示第i个命令执行以后所有控制点到机器人的曼哈顿距离之和。
【输入样例】
3 5
0 0
1 1
1 -1
SIJJZ
【输出样例】
5
4
3
4
5
【数据范围与约定】
对于40%的数据:n<=1000,m<=5000。
对于100%的数据:n <= 100000, m <= 300000。

(感觉这题很乱搞???)

【分析】

考虑机器人每走一步都以其为原点建立平面直角坐标系,先暴力每个点到机器人的曼哈顿距离之和。
然后预处理出 x 轴上下方、y轴左右方、 xy 轴上以及每一行每一列的固定点个数。
记此时机器人的坐标为 (tx,ty) ,则当机器人向右走时, y 轴以及y轴左方的固定点到机器人的距离都增加 1 y轴右方的固定点到机器人的距离都减少1,同时现在 y 轴就变为y=tx+1 y 轴左方的固定点个数要加上原y轴的固定点个数, y 轴右方的固定点个数要减去原y轴的固定点个数,现在 y 轴上的固定点个数可以通过之前每一行每一列的预处理更新。而当机器人向其它方向走时,也是同理。
这里每一行每一列的固定点个数用的是map存储,这样的总复杂度为 O(mlogn)

【代码】
#include <iostream>
#include <cstdio>
#include <map>

using namespace std;
typedef long long ll;

map<int, int> a, b;

const int N = 1e5 + 5;

char s[N * 3];
int lft, rig, upt, dwn, ex, ey, tx, ty, n, m; 
ll Ans;

inline int Abs(const int &x)
{
    return x < 0 ? -x : x;
}

inline int get()
{
    char ch; int res = 0; bool f = false;
    while (((ch = getchar()) < '0' || ch > '9') && ch != '-');
    if (ch == '-') f = true;
     else res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return f ? -res : res;
}

inline void put(ll x)
{
    if (x > 9) put(x / 10);
    putchar(x % 10 + 48);
}

int main()
{
    freopen("robot.in", "r", stdin);
    freopen("robot.out", "w", stdout);
    n = get(); m = get(); int u, v;
    for (int i = 1; i <= n; ++i)
    {
        u = get(); v = get();
        if (u < 0) lft++;
         else if (u > 0) rig++;
          else ey++;
        if (v > 0) upt++;
         else if (v < 0) dwn++;
          else ex++; 
        Ans += Abs(u) + Abs(v);
        a[u]++; b[v]++;
    }
    scanf("%s", s + 1);
    for (int i = 1; i <= m; ++i)
    {
        switch(s[i])
        {
            case 'S': ty++;
              Ans += dwn - upt + ex; 
              dwn += ex;
              upt -= (ex = b[ty]);
              break;
             case 'J': ty--;
              Ans += upt - dwn + ex;
              upt += ex;
              dwn -= (ex = b[ty]);
              break;
             case 'I': tx++;
              Ans += lft - rig + ey;
              lft += ey;
              rig -= (ey = a[tx]);  
              break;
             case 'Z': tx--;
              Ans += rig - lft + ey;
              rig += ey;
              lft -= (ey = a[tx]);
              break;
        }
        put(Ans), putchar('\n');
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值