poj 1195/2155 二维树状数组(子矩阵值之和)

题意:一个由数字构成的大矩阵,开始是全0,能进行如下两种操作:1、对矩阵里的某个数加上一个整数(可正可负);2、查询某个子矩阵里所有数字的和。要求对每次查询,输出结果。
思路:二维树状数组。二维树状数组的思路和一维的比较类似。树状数组C[x][y] = Σ {a[i][j]},其中x-lowbit[x]+1 <= i <=x;y-lowbit[y]+1 <= j <=y。
Sum(x,y) = Σ {C [i][j]} (从[1,1]到[x,y]这个矩阵里的所有元素的和),其中
i1=x, i2 = i1-lowbit(i1),...,ik = ik-1-lowbit(ik-1)...;
j1=y, j2 = j1-lowbit(j1),...,jk = jk-1-lowbit(jk-1)...。

用于快速求数字子矩阵的和,更新和查询的时间复杂度都是log(n)*log(m) (n,m分别为两维的大小)。

一个二维树状数组元素的例子如下图:


此题需要注意的两点:1、与一维类似,二维树状数组的两维下标都只能从1开始,所以如果区域的横纵坐标从0开始,那么要人工加1处理。2、sum(x,y)计算的是(1,1)到(x,y)这个子矩阵之间值的和。所以求以(a,b)和(c,d)为矩阵两对角的计算方法要注意将边界值计算进去,即sum(c,d)-sum(c,b-1)-sum(a-1,d)+sum(a-1,b-1)


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1040
#define INF 0x3fffffff
using namespace std;
int tree[N][N];
int n;
int lowbit(int x){
    return x&(-x);
}
void add(int a,int b,int x){
    int i,j;
    for(i = a;i<=n;i+=lowbit(i))
        for(j = b;j<=n;j+=lowbit(j))
            tree[i][j] += x;
}
int sum(int a,int b){
    int i,j,res=0;
    for(i = a;i>=1;i-=lowbit(i))
        for(j = b;j>=1;j-=lowbit(j))
            res += tree[i][j];
    return res;
}
int main(){
    int cmd,l,b,r,t,a,y,x;
    while(scanf("%d",&cmd) && cmd<3){
        if(cmd == 0){
            scanf("%d",&n);
            memset(tree,0,sizeof(tree));
        }else if(cmd == 1){
            scanf("%d %d %d",&x,&y,&a);
            add(x+1,y+1,a);
        }else{
            scanf("%d %d %d %d",&l,&b,&r,&t);
            printf("%d\n",sum(r+1,t+1)-sum(l,t+1)-sum(r+1,b)+sum(l,b));
        }
    }
    return 0;
}

2155

题意:N * N 的矩阵,每个元素要么是0,要么是1,开始全0。不断进行两种操作:
1) C x1 y1 x2 y2 表示要将左上角为(x1,y1),右下 角为(x2,y2)的子矩阵里的全部元素都取反(0 变1,1变0) (1<=x1<=x2<=n,1<=y1<=y2<=n)
2) Q x y 查询(x,y)

思路:对四个角进行标记,查询时看该点左方和上方标记的点数之和是奇数还是偶数即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 1005
int tree[N][N];
int n,q,T;
int lowbit(int x){
    return x&(-x);
}
void add(int x,int y){
    for(int i = x;i<=n;i+=lowbit(i))
        for(int j = y;j<=n;j+=lowbit(j))
            tree[i][j] += 1;
}
int sum(int x,int y){
    int i,j,res=0;
    for(i = x;i>=1;i-=lowbit(i))
        for(j = y;j>=1;j-=lowbit(j))
            res += tree[i][j];
    return res;
}
int main(){
    scanf("%d",&T);
    while(T--){
        int a,b,c,d;
        scanf("%d %d",&n,&q);
        clc(tree,0);
        while (q--) {
            char ch;
            getchar();
            ch = getchar();
            if(ch == 'C'){
                scanf("%d %d %d %d",&a,&b,&c,&d);
                add(a,b);
                add(a,d+1);
                add(c+1,b);
                add(c+1,d+1);
            }else{
                scanf("%d %d",&a,&b);
                printf("%d\n",(sum(a,b))%2);
            }
        }
        putchar('\n');
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值