【题解】HDU1892 See you~

3 篇文章 0 订阅

传送门

题目大意:你有三种操作:

  • S x1 y1 x2 y2 S   x 1   y 1   x 2   y 2 表示查询从 (x1,y1) ( x 1 , y 1 ) (x2,y2) ( x 2 , y 2 ) 之间这块矩形中所有数之和。
  • A x1 y1 n1 A   x 1   y 1   n 1 表示给 (x1,y1) ( x 1 , y 1 ) 那堆增加 n1 n 1 本书
  • D x1 y1 n1 D   x 1   y 1   n 1 表示给 (x1,y1) ( x 1 , y 1 ) 那堆减去 n1 n 1 本书(书目不足的话就全部拿走)
  • M x1 y1 x2 y2 n1 M   x 1   y 1   x 2   y 2   n 1 表示从 (x1,y1) ( x 1 , y 1 ) 这堆中拿出 n1 n 1 本书搬到 (x2,y2) ( x 2 , y 2 ) 上(书目不足全部搬走)

首先拿到题目,有很多修改操作,直接前缀和维护很难修改,二维线段树写起来又非常繁琐,现在可供我们使用的就是二维树状数组。

树状数组还不是很清楚的请戳

树状数组的一大优点就是非常容易拓展到高维。我们先定义二维下的问题,类似地,大家可以自行定义更高维的情况。
定义一个二维数组 a[1..n,1..n] a [ 1.. n , 1.. n ] ,并维护以下两个操作:

  • 修改:给 a[i,j] a [ i , j ] 加上一个增量 delta d e l t a
  • 查询:询问左上角 a[1..x,1..y] a [ 1.. x , 1.. y ] 的和,即 xi=1yj=1a[i,j] ∑ i = 1 x ∑ j = 1 y a [ i , j ]

我们同样用一个二维数组 sum s u m 维护被分割的“子集”之和,模仿一维情形下的定义,将二维的 sum s u m 数组定义如下: sum[x,y]=xi=xc(x)+1yj=yc(y)+1a[i,j] s u m [ x , y ] = ∑ i = x − c ( x ) + 1 x ∑ j = y − c ( y ) + 1 y a [ i , j ]
不妨做这样的类比:当 sum s u m 数组的第一维坐标固定后, sum s u m 数组又可以看做以为的情形,只是这个一位数组是记录的二维数组 a a 对应行的若干列合并之后的部分和。
看了代码之后应该就很清晰了。

int c[MAXSIZE][MAXSIZE];
namespace BIT_2D {
    inline int lowbit(int x) { 
        return x & -x; 
    }
    void update(int x, int y, int d) {  
        for (int i = x; i < MAXSIZE; i += lowbit(i))
            for (int j = y; j < MAXSIZE; j += lowbit(j)) 
                c[i][j] += d;
    }
    int query(int x, int y) {  
        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];
        return ret;  
    }  
}
using namespace BIT_2D;

另外建议做了这道题之后去看看HDU3584,相信对你锻炼树状数组拓展到高维的能力会很有帮助。
下面来考虑怎么用二维树状数组实现上面几个操作。

  • S 操作,就是查询,我们先将两个数对调整为 x1<x2 x 1 < x 2 y1<y2 y 1 < y 2 , 便于进行查询,查询方法和二维前缀和完全一样。实际上就是容斥原理的应用。

    int calc(int x1, int y1, int x2, int y2) {
            return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
        }
    • ADM A , D , M 操作,单点修改,直接调用 update() u p d a t e ( ) 即可,若是减去就添个负号。
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXSIZE 1010
    int c[MAXSIZE][MAXSIZE];
    namespace BIT_2D {
        inline int lowbit(int x) { 
            return x & -x; 
        }
        void update(int x, int y, int d) {  
            for (int i = x; i < MAXSIZE; i += lowbit(i))
                for (int j = y; j < MAXSIZE; j += lowbit(j)) 
                    c[i][j] += d;
        }
        int query(int x, int y) {  
            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];
            return ret;  
        }  
        int calc(int x1, int y1, int x2, int y2) {
            return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
        }
    }
    using namespace BIT_2D;
    int main() {
        int T;
        scanf("%d", &T);
        for (int t = 1; t <= T; t++) {
            printf("Case %d:\n", t);
            memset(c, 0, sizeof c);
            for (int i = 1; i < MAXSIZE; i++)
                for (int j = 1; j < MAXSIZE; j++)
                    update(i, j, 1);
            int m;
            scanf("%d", &m);
            for (int i = 1; i <= m; i++) {
                int x1, y1, x2, y2, n;
                char ch = getchar();
                while (ch != 'S' && ch != 'A' && ch != 'D' && ch != 'M') ch = getchar();
                if (ch == 'S') {
                    scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
                    x1++; y1++; x2++; y2++;
                    if (x1 > x2) std::swap(x1, x2);
                    if (y1 > y2) std::swap(y1, y2);
                    printf("%d\n", calc(x1, y1, x2, y2));
                }
                else if (ch == 'A') {
                    scanf("%d%d%d", &x1, &y1, &n);
                    x1++; y1++;
                    update(x1, y1, n);
                }
                else if (ch == 'D') {
                    scanf("%d%d%d", &x1, &y1, &n);
                    x1++; y1++; 
                    n = std::min(n, calc(x1, y1, x1, y1));
                    update(x1, y1, -n);
                }
                else if (ch == 'M') {
                    scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &n);
                    x1++; y1++; x2++; y2++;
                    n = std::min(n, calc(x1, y1, x1, y1));
                    update(x1, y1, -n);
                    update(x2, y2, n);
                }
            }
        }
        return 0;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值