2018 ICPC 沈阳 Best ACMer Solves the Hardest Problem(暴力)
发布日期:2021-05-07 02:31:08 浏览次数:19 分类:原创文章

本文共 4665 字,大约阅读时间需要 15 分钟。


题目大意

在二维平面给出 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;}
上一篇:HDU - 5950 Recursive sequence(非常规矩阵快速幂)
下一篇:Codeforces Round #677 (Div. 3) F. Zero Remainder Sum(dp好题)

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年04月10日 13时04分20秒