【题解】HDU1892 See you~

传送门

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

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

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

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

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

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

我们同样用一个二维数组 s u m 维护被分割的“子集”之和,模仿一维情形下的定义,将二维的 s u m 数组定义如下: s u m [ x , y ] = i = x c ( x ) + 1 x j = y c ( y ) + 1 y a [ i , j ]
不妨做这样的类比:当 s u m 数组的第一维坐标固定后, s u m 数组又可以看做以为的情形,只是这个一位数组是记录的二维数组 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 操作,就是查询,我们先将两个数对调整为 x 1 < x 2 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);
    }
  • A D M 操作,单点修改,直接调用 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;
}

猜你喜欢

转载自blog.csdn.net/diogenes_/article/details/80636216