题目大意
在二维平面给出 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 (x0−x)2+(y0−y)2=k,而且 k k k的范围是 1 e 7 1e7 1e7,考虑枚举 k \sqrt{k} k范围内的所有 d x = x 0 − x dx = x_0-x dx=x0−x,判断 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;
}