[二维树状数组]计数问题

题目描述

 一个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

提示

对于30%的数据n,m<=30,q<=10000
对于100%的数据 n,m<=300,q<=100000,1<=c<=100

思路:求询问区间内某一个值出现的次数;
单点更新:若在某个位置(i,j)出现了某个数k则标记为1(三位数组c[i][j][k]记录这个信息),原先的数标记为0;
区间求和:求区间内某个数k出现次数即调用getsum(i,j,k)求和c[..][..][k]数组得到(1,1)-(i,j)内k出现的次数;结果是getsum(xx,yy,k)-getsum(xx,y-1,k)-getsum(x-1,yy,k)+getsum(x-1,y-1,k);(容斥原理)
AC代码:
#include <iostream>
#include<cstdio>
#define lowbit(x) x&(-x)
using namespace std;

int n,m;
int Map[310][310];
int c[310][310][110];

void add(int x,int y,int k,int val){
   for(int i=x;i<=n;i+=lowbit(i)){
     for(int j=y;j<=m;j+=lowbit(j)){
        c[i][j][k]+=val;
     }
   }
}

int getsum(int x,int y,int k){
   int ret=0;
   for(int i=x;i>0;i-=lowbit(i)){
     for(int j=y;j>0;j-=lowbit(j)){
        ret+=c[i][j][k];
     }
   }
   return ret;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&Map[i][j]);
            add(i,j,Map[i][j],1);
        }
    }
    int q;
    scanf("%d",&q);
    while(q--){
        int op;
        scanf("%d",&op);
        if(op==1){
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            add(x,y,Map[x][y],-1);
            add(x,y,k,1);
            Map[x][y]=k;
        }
        if(op==2){
            int x,xx,y,yy,k;
            scanf("%d%d%d%d%d",&x,&xx,&y,&yy,&k);
            printf("%d
",getsum(xx,yy,k)-getsum(x-1,yy,k)-getsum(xx,y-1,k)+getsum(x-1,y-1,k));
        }
    }
    return 0;
}
 
转载请注明出处:https://www.cnblogs.com/lllxq/
原文地址:https://www.cnblogs.com/lllxq/p/9095555.html