关键词:前缀和 树状数组 相关题目:[JSOI2009]计数问题

题目描述

一个n*m的方格,初始时每个格子有一个整数权值。接下来每次有2种操作:

  • 改变一个格子的权值;

  • 求一个子矩阵中某种特定权值出现的个数。

输入输出格式

输入格式:

第一行有两个数N,M。

接下来N行,每行M个数,第i+1行第j个数表示格子(i,j)的初始权值。

接下来输入一个整数Q。

之后Q行,每行描述一个操作。

操作1:“1 x y c”(不含双引号)。表示将格子(x,y)的权值改成c(1<=x<=n,1<=y<=m,1<=c<=100)。

操作2:“2 x1 x2 y1 y2 c”(不含双引号,x1<=x2,y1<=y2)。表示询问所有满足格子颜色为c,且x1<=x<=x2,y1<=y<=y2的格子(x,y)的个数。

输出格式: 对于每个操作2,按照在输入中出现的顺序,依次输出一行一个整数表示所求得的个数。

输入输出样例

输入样例

3 3
1 2 3
3 2 1
2 1 3
3
2 1 2 1 2 1
1 2 3 2
2 2 3 2 3 2

输出样例

1
2

Solution

我们在每一行建立一个树状数组,每次修改的操作为 O(log n),询问 O(nlog n)。只能过 30% 的数据,而显然询问的复杂度太高。

考虑差分以及前缀和,我们在上面的算法的基础上的 建立树状数组。之前我们在 O(log n) 的时间内得到 (x,y1) 到 (x,y2) 内的某种颜色个数,改动后我们可以在 O(log2 n) 的时间内得到 (1,1)(x1,y1) 内某种颜色的个数。

修改和查询的复杂度均为 O(log2 n) 。

AC代码

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
#include <cstdio>
#define re register int
#define rep(i,a,b) for(re i=(a);i<=(b);++i)

int n, m, Q;
int v[302][302];
int t[302][302][102];

inline int lowbit(int x) {return x&(-x);}

inline void update(int x, int y, int k, int d) {
for(re i=x;i<=n;i+=lowbit(i))
for(re j=y;j<=m;j+=lowbit(j))
t[i][j][k] += d;
}
inline int query(int x, int y, int k) {
int res = 0;
for(re i=x;i;i-=lowbit(i))
for(re j=y;j;j-=lowbit(j))
res += t[i][j][k];
return res;
}

int main() {
scanf("%d%d", &n, &m);
rep(i,1,n)
rep(j,1,m)
scanf("%d", &v[i][j]), update(i, j, v[i][j], 1);

int opt, x1, x2, y1, y2, c, ans;
scanf("%d", &Q);
while(Q--) {
scanf("%d", &opt);
if(opt==1) {
scanf("%d%d%d", &x1, &y1, &c);
update(x1, y1, v[x1][y1], -1), update(x1, y1, c, 1);
v[x1][y1] = c;
}
else {
scanf("%d%d%d%d%d", &x1, &x2, &y1, &y2, &c);
ans = query(x2, y2, c) + query(x1-1, y1-1, c) -
query(x2, y1-1, c) - query(x1-1, y2, c);
printf("%d\n", ans);
}
}
}