2018 ICPC 沈阳 Best ACMer Solves the Hardest Problem(暴力)

传送门


题目大意

在二维平面给出 n n n个点,每个点都有一个权值,且横纵坐标是正整数且范围均是 [ 1 , 6000 ] [1,6000] [1,6000],现在有四种操作:

  • 插入一个不存在的点
  • 删除一个已经存在的点
  • 输入一个点和长度 k k k,权值 w w w,距离该点欧几里得距离为 k \sqrt{k} k 的所有点的权值都增加 k k k
  • 输入一个点和长度 k k k,求出距离该点欧几里得距离为 k \sqrt{k} k 的所有点的权值和
解题思路

不难发现,该题的坐标范围很小,时间给了 12 s 12s 12s而且内存给的很大 1024 M 1024M 1024M,因此考虑暴力。但是一开始写了个假算法,我们想的是距离点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) k \sqrt{k} k 的点满足圆的方程 ( x 0 − x ) 2 + ( y 0 − y ) 2 = k (x_0-x)^2+(y_0-y)^2=k (x0x)2+(y0y)2=k,而且 k k k的范围是 1 e 7 1e7 1e7,考虑枚举 k \sqrt{k} k 范围内的所有 d x = x 0 − x dx = x_0-x dx=x0x,判断 d y 2 dy^2 dy2是否是平方数,然后判断是否在范围内等等,这样统计,最后得出的时间复杂度为 1 0 9.5 10^{9.5} 109.5,感觉可以过但是还是被卡了…

实际上内存这么大, k k k最多为 1 e 7 1e7 1e7,完全没必要枚举 d x dx dx,只需要预处理每个 k k k对应的 { d x , d y } \{dx,dy\} {dx,dy}对,圆上的整点个数是常数个,这样预处理后每次都是常数的枚举,显然这个算法是可以通过的。至于权值,显然一个二维数组就可以存下,但是本题还有一个卡点,注意 m e m s e t ( ) memset() memset()的时间复杂度是 O ( n ) O(n) O(n),如果每次都对一个 1 e 7 1e7 1e7的数组预处理, T = 1000 T=1000 T=1000次操作就被卡爆了,这时我们也可以利用本题输入和修改的点是有限的,那么就依靠内存,每次存下上次出现的所有点然后初始化权值为零,真是血的教训!

这次训练赛的这道题让我们以后估计时间复杂度更加谨慎,感觉可以过却超时的题目一定要从题目给出的各个范围的数下手,有可能有意想不到的时间优化。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e7 + 10;
const int inf = 1e7;

int T, n, m;
ll lastAns = 0;

int val[6100][6100];
vector<pii> points[maxn], all;

struct hash_pair {
    template<class T1, class T2>
    size_t operator()(const pair<T1, T2> &p) const {
        auto hash1 = hash<T1>{}(p.first);
        auto hash2 = hash<T2>{}(p.second);
        return hash1 ^ hash2;
    }
};

unordered_map<pii, bool, hash_pair> vis;

void init() {
    for (int i = 0; i < 3300; i++) {
        for (int j = 0; j < 3300; j++) {
            if (i * i + j * j <= inf) {
                points[i * i + j * j].push_back(pii(i, j));
            }
        }
    }
}

inline bool check(int x, int y) {
    if (x <= 0 || x > 6000 || y <= 0 || y > 6000) return 0;
    if (!val[x][y]) return 0;
    return 1;
}

inline void add(int x, int y, int k, int w) {
    vis.clear();
    for (auto p: points[k]) {
        int dx = p.first, dy = p.second;
        if (check(x + dx, y + dy) && !vis[{x + dx, y + dy}]) val[x + dx][y + dy] += w, vis[{x + dx, y + dy}] = 1;
        if (check(x + dx, y - dy) && !vis[{x + dx, y - dy}]) val[x + dx][y - dy] += w, vis[{x + dx, y - dy}] = 1;
        if (check(x - dx, y + dy) && !vis[{x - dx, y + dy}]) val[x - dx][y + dy] += w, vis[{x - dx, y + dy}] = 1;
        if (check(x - dx, y - dy) && !vis[{x - dx, y - dy}]) val[x - dx][y - dy] += w, vis[{x - dx, y - dy}] = 1;
    }
}

inline ll getSum(int x, int y, int k) {
    ll ans = 0;
    vis.clear();
    for (auto p:points[k]) {
        int dx = p.first, dy = p.second;
        if (check(x + dx, y + dy) && !vis[{x + dx, y + dy}]) ans += val[x + dx][y + dy], vis[{x + dx, y + dy}] = 1;
        if (check(x + dx, y - dy) && !vis[{x + dx, y - dy}]) ans += val[x + dx][y - dy], vis[{x + dx, y - dy}] = 1;
        if (check(x - dx, y + dy) && !vis[{x - dx, y + dy}]) ans += val[x - dx][y + dy], vis[{x - dx, y + dy}] = 1;
        if (check(x - dx, y - dy) && !vis[{x - dx, y - dy}]) ans += val[x - dx][y - dy], vis[{x - dx, y - dy}] = 1;
    }
    return ans;
}

int main() {
    init();
    scanf("%d", &T);
    for (int Case = 1; Case <= T; Case++) {
        lastAns = 0;
        for (auto p:all) {
            val[p.first][p.second] = 0;
        }
        all.clear();
        scanf("%d%d", &n, &m);
        printf("Case #%d:\n", Case);
        for (int i = 1, x, y, w; i <= n; i++) {
            scanf("%d%d%d", &x, &y, &w);
            val[x][y] = w;
            all.push_back({x, y});
        }
        for (int i = 1, op, x, y, k, w; i <= m; i++) {
            scanf("%d", &op);
            if (op == 1) {
                scanf("%d%d%d", &x, &y, &w);
                x = ((x + lastAns) % 6000) + 1;
                y = ((y + lastAns) % 6000) + 1;
                val[x][y] = w;
                all.push_back({x, y});
            } else if (op == 2) {
                scanf("%d%d", &x, &y);
                x = ((x + lastAns) % 6000) + 1;
                y = ((y + lastAns) % 6000) + 1;
                val[x][y] = 0;
            } else if (op == 3) {
                scanf("%d%d%d%d", &x, &y, &k, &w);
                x = ((x + lastAns) % 6000) + 1;
                y = ((y + lastAns) % 6000) + 1;
                add(x, y, k, w);
            } else if (op == 4) {
                scanf("%d%d%d", &x, &y, &k);
                x = ((x + lastAns) % 6000) + 1;
                y = ((y + lastAns) % 6000) + 1;
                ll ans = getSum(x, y, k);
                lastAns = ans;
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值