Codeforces 707E Garlands

链接

传送门

题意

给出$n \times m$的矩阵,矩阵中的数分为了$k$个分量,其中$1 \leq n, m, k \leq 2000$。每个点都有各自的权值。有两种操作:

  • 修改一个分量的开关状态。
  • 查询一个矩形内所有开着的点的权值和并输出。

其中查询操作不超过2000次。

思路

记录查询时分量的开关状态,然后用二维树状数组计算出分量对每个查询的贡献。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define X first
#define Y second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int maxn = 2005;
char cmd[maxn];
LL bit[maxn][maxn], ans[maxn];
int n, m, a[maxn][maxn];
int u[maxn], l[maxn], d[maxn], r[maxn];
bool vis[maxn][maxn], cur[maxn];
vector<pii> g[maxn];
LL sum(int x, int y) {
LL res = 0;
for (int i = x; i > 0; i -= i & -i) {
for (int j = y; j > 0; j -= j & -j) {
res += bit[i][j];
}
}
return res;
}
void add(int x, int y, int d) {
for (int i = x; i <= n; i += i & -i) {
for (int j = y; j <= m; j += j & -j) {
bit[i][j] += d;
}
}
}
int main() {
int k;
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= k; ++i) {
int x, y, z;
scanf("%d", &z);
for (int j = 0 ; j < z; ++j) {
scanf("%d%d", &x, &y);
g[i].emplace_back(make_pair(x, y));
scanf("%d", &a[x][y]);
}
}
int q, num = 0;
scanf("%d", &q);
for (int i = 0; i < q; ++i) {
scanf("%s", cmd);
if (cmd[0] == 'A') {
++num;
scanf("%d%d%d%d", &u[num], &l[num], &d[num], &r[num]);
for (int j = 1; j <= k; ++j) {
vis[j][num] = !cur[j];
}
} else {
int x;
scanf("%d", &x);
cur[x] = !cur[x];
}
}
for (int i = 1; i <= k; ++i) {
for (pii& u: g[i]) {
add(u.X, u.Y, a[u.X][u.Y]);
}
for (int j = 1; j <= num; ++j) {
if (vis[i][j]) {
ans[j] += sum(d[j], r[j]) - sum(d[j], l[j] - 1) - sum(u[j] - 1, r[j]) + sum(u[j] - 1, l[j] - 1);
}
}
for (pii& u: g[i]) {
add(u.X, u.Y, -a[u.X][u.Y]);
}
}
for (int i = 1; i <= num; ++i) {
printf("%lld\n", ans[i]);
}
return 0;
}

支付宝扫码领红包