题目链接:hdu - 4456
题意
        
\;\;\;\;
在一个二维格子中,给出一些位置的权值,查询某些位置的权值和,初值为
0
0
0。
        
\;\;\;\;
有两种操作:
        
\;\;\;\;
1.
(
x
,
y
,
z
)
(x,y,z)
(x,y,z) 位置
(
x
,
y
)
(x,y)
(x,y) 的权值
d
(
x
,
y
)
+
z
d(x,y) + z
d(x,y)+z.
        
\;\;\;\;
2.
(
x
,
y
,
k
)
(x,y,k)
(x,y,k), 求
∑
∣
i
−
x
∣
+
∣
j
−
y
∣
≤
k
d
(
i
,
j
)
\sum_{|i-x|+|j-y|\leq k} d(i,j)
∑∣i−x∣+∣j−y∣≤kd(i,j), 即与点
(
x
,
y
)
(x,y)
(x,y) 的曼哈顿距离不超过
k
k
k 的点 的 权值和。
        
\;\;\;\;
格子大小
N
⋅
N
N \cdot N
N⋅N, 操作数 M.
        
\;\;\;\;
1
≤
n
≤
1
0
5
,
1
≤
m
≤
8
∗
1
0
4
,
−
100
≤
z
≤
100
,
1
≤
k
≤
2
⋅
N
−
1
1 \leq n \leq 10^{5}, 1 \leq m \leq 8*10^{4}, -100 \leq z \leq 100, 1 \leq k \leq 2\cdot N - 1
1≤n≤105,1≤m≤8∗104,−100≤z≤100,1≤k≤2⋅N−1.
思路
        
\;\;\;\;
对于查询操作
(
x
,
y
,
k
)
(x,y,k)
(x,y,k),与点
(
x
,
y
)
(x,y)
(x,y) 的曼哈顿距离不超过
k
k
k 的点,会组成一个正方形,四个点分别是
(
x
,
y
+
k
)
,
(
x
,
y
−
k
)
,
(
x
−
k
,
y
)
,
(
x
+
k
,
y
)
(x,y+k),(x,y-k),(x-k,y),(x+k,y)
(x,y+k),(x,y−k),(x−k,y),(x+k,y), 将坐标旋转45°,这些点组成的区域为正方形左下角至右上角:
(
x
+
y
−
k
,
x
−
y
−
k
)
→
(
x
+
y
+
k
,
x
−
y
−
k
)
(x+y-k,x-y-k) \rightarrow (x+y+k, x-y-k)
(x+y−k,x−y−k)→(x+y+k,x−y−k). 于是问题就变成了一般的求矩阵和。
        
\;\;\;\;
如果
N
N
N 比较小的话,二维数组数组开起 Over! 但是现在
N
N
N 比较大。
        
\;\;\;\;
但是给的点很少!
        
\;\;\;\;
将二维树状数组中可能会用到的所有点一维化,存下来(有点像 Hash?)。这样最多需要存
M
∗
l
o
g
2
N
∗
l
o
g
2
N
M * log2N * log2N
M∗log2N∗log2N 个点。离散化一下,排序,去重。
代码
/*
nero
http://acm.hdu.edu.cn/showproblem.php?pid=4456
2019-09-03 20:35:50
WA 14
*/
#include <map>
#include <time.h>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int, int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 20000000;
int W, N, M, cnt = 0;
int H[MAXN], c[MAXN];
struct node {
int op, x, y, z;
}a[80005];
int lowbit(int x) {
return x & (-x);
}
void func(int x, int y) {
for(int i = x; i <= W; i += lowbit(i)) {
for(int j = y; j <= W; j += lowbit(j)) {
H[++cnt] = i * W + j; // 所有可能用到的点 *QQ*
c[cnt] = 0;
}
}
return;
}
void update(int x, int y, int z) {
for(int i = x; i <= W; i += lowbit(i)) {
for(int j = y; j <= W; j += lowbit(j)) {
int p = lower_bound(H + 1, H + cnt + 1, i * W + j) - H;
c[p] += z;
}
}
return;
}
int Query(int x, int y) {
int sum = 0;
for(int i = x; i >= 1; i -= lowbit(i)) {
for(int j = y; j >= 1; j -= lowbit(j)) {
int p = lower_bound(H + 1, H + cnt + 1, i * W + j) - H;
if(H[p] == i * W + j)
sum += c[p];
}
}
return sum;
}
int main() {
while(scanf("%d", &N) != EOF) {
if(N == 0) break;
scanf("%d", &M);
W = N + N;
cnt = 0;
for(int i = 1; i <= M; i++) {
scanf("%d%d%d%d", &a[i].op,&a[i].x, &a[i].y, &a[i].z);
if(a[i].op == 1) {
func(a[i].x + a[i].y, a[i].x - a[i].y + N); // 旋转 *QQ*
}
}
sort(H + 1, H + cnt + 1);
// cnt = unique(H + 1, H + cnt + 1) - H; // H[0] = 0 WA++
cnt = unique(H + 1, H + cnt + 1) - H - 1;
for(int i = 1; i <= M; i++) {
if(a[i].op == 1) {
update(a[i].x + a[i].y, a[i].x - a[i].y + N, a[i].z);
}
else {
int x1 = max(0, a[i].x + a[i].y - a[i].z);
int y1 = max(0, a[i].x - a[i].y + N - a[i].z);
int x2 = min(W, a[i].x + a[i].y + a[i].z);
int y2 = min(W, a[i].x - a[i].y + N + a[i].z);
int ans = Query(x2, y2) - Query(x2, y1-1) - Query(x1-1, y2) + Query(x1-1, y1-1);
printf("%d\n", ans);
}
}
}
return 0;
}
本文介绍了一种解决二维格子中特定位置权值查询的问题,通过使用二维树状数组和坐标旋转技巧,将曼哈顿距离内的权值和查询转化为常规的矩阵和查询,适用于大规模数据集和较少的更新操作。

499

被折叠的 条评论
为什么被折叠?



