【題解】HDU1892 See you~

傳送門

題目大意:你有三種操作:

  • S x1 y1 x2 y2 表示查詢從 (x1,y1)(x2,y2) 之間這塊矩形中所有數之和。
  • A x1 y1 n1 表示給 (x1,y1) 那堆增加 n1 本書
  • D x1 y1 n1 表示給 (x1,y1) 那堆減去 n1 本書(書目不足的話就全部拿走)
  • M x1 y1 x2 y2 n1 表示從 (x1,y1) 這堆中拿出 n1 本書搬到 (x2,y2) 上(書目不足全部搬走)

首先拿到題目,有很多修改操作,直接前綴和維護很難修改,二維線段樹寫起來又非常繁瑣,現在可供我們使用的就是二維樹狀數組。

樹狀數組還不是很清楚的請戳

樹狀數組的一大優點就是非常容易拓展到高維。我們先定義二維下的問題,類似地,大家可以自行定義更高維的情況。
定義一個二維數組 a[1..n,1..n] ,並維護以下兩個操作:

  • 修改:給 a[i,j] 加上一個增量 delta
  • 查詢:詢問左上角 a[1..x,1..y] 的和,即 i=1xj=1ya[i,j]

我們同樣用一個二維數組 sum 維護被分割的“子集”之和,模仿一維情形下的定義,將二維的 sum 數組定義如下: sum[x,y]=i=xc(x)+1xj=yc(y)+1ya[i,j]
不妨做這樣的類比:當 sum 數組的第一維座標固定後, sum 數組又可以看做以爲的情形,只是這個一位數組是記錄的二維數組 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<x2y1<y2 , 便於進行查詢,查詢方法和二維前綴和完全一樣。實際上就是容斥原理的應用。
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 操作,單點修改,直接調用 update() 即可,若是減去就添個負號。
#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章