树套树之线段树套线段树(POJ2155 Matrix)

表示知道线段树的人做一道二维线段树就应当会了。。。
所以这里直接给出例题。

Matrix(POJ 2155) 题目传送门
题目描述:
给出一个N*N的矩阵A, 它的元素都是0或1,A[i,j]表示第i行第j列的数字。开始时,A[i,j]均为0(1<=i,j<=N)。我们可以对它进行以下两种操作:
1.C x1 y1 x2 y2(1<=x1<=x2<=N,1<=y1<=y2<=N)改变以(x1,y1)为左上角,(x2,y2)为右下角的矩形内所有元素的值(1变为0,0变为1)。
2.Q x y(1<=x,y<=N)询问A[i,j]的值。

分析:
由于数据太大,直接建立二维数组进行操作时间复杂度为Θ(T*N*N),显然会超时,我们可以考虑使用线段树套线段树(二维线段树)来解决这一类问题。首先对横坐标x建立一棵线段树,这棵树的每一个节点表示横坐标的一个区间,再在每一个节点上对纵坐标y建立一棵线段树,这棵线段树的每一个节点都表示一个矩形的区域。根据线段树的操作完成查询和更改。
如果使用结构体指针建树,程序将十分复杂且容易出错。这里使用了一个技巧,用一个数组g[i][j]来表示第一棵线段树第i个节点上的第二棵线段树第j个节点所对应的区域为1或0。这样就不需要使用结构体了。
程序如下:

#include
#include
bool g[4010][4010];
int n, m, T, ans;
void update_y(int i, int l, int r, int j, int y1, int y2) {
    if(l == y1 && r == y2){
        g[i][j] ^= 1;
        return;
    }
    int mid=(l + r) >> 1;
    if(mid >= y2) update_y(i, l, mid, 2 * j, y1, y2);
    else if(y1 > mid) update_y(i, mid+1, r, 2 * j + 1, y1, y2);
    else{
        update_y(i, l, mid, 2 * j, y1, mid);
        update_y(i, mid + 1, r, 2 * j + 1, mid + 1, y2);
    }
}  
void update_x(int i, int l, int r, int x1, int x2, int y1, int y2) {
    if(l == x1 && r == x2) {
        update_y(i, 1, n, 1, y1, y2);
        return;
    }
    int mid = (l + r) >> 1;
    if(mid >= x2) update_x(2 * i, l, mid, x1, x2, y1, y2);
    else if(x1 > mid) update_x(2 * i + 1, mid + 1, r, x1, x2, y1, y2);
    else {
        update_x(2 * i, l, mid, x1, mid, y1, y2);
        update_x(2 * i + 1, mid + 1, r, mid + 1, x2, y1, y2);
    }  
}  
void query_y(int i, int l, int r, int j, int y) {
    ans ^= g[i][j];
    if(l==r) return;
    int mid = (l + r) >> 1;
    if(mid >= y)
        query_y(i, l, mid, 2 * j, y);
    else
        query_y(i, mid + 1, r, 2 * j + 1, y);
}
void query_x(int i, int l, int r, int x, int y) {
    query_y(i, 1, n, 1, y);
    if(l == r)
        return;
    int mid = (l + r) >> 1;
    if(mid >= x)
        query_x(2 * i, l, mid, x, y);
    else
        query_x(2 * i + 1, mid + 1, r, x, y);
}  
int main() {  
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(g, 0, sizeof(g));
        for(int i = 0; i < m; i++) {
            char s[2];
            scanf("%s", s);
            if(s[0] == 'C') {
                int x1, x2, y1, y2;
                scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
                update_x(1, 1, n, x1, x2, y1, y2);
            }
            else {
                ans = 0;
                int x, y;
                scanf("%d%d", &x, &y);
                query_x(1, 1, n, x, y);
                printf("%d\n", ans);
            }
        }
        if(T) printf("\n");  
    }  
    return 0;  
}

以上程序的时间复杂度为Θ(T*logN*logN),比之前的想法快了不少,这道题同样可以用二维树状数组来解决,时间复杂度相同,实现方法也类似。此题是线段树套线段树的典型例题,许多有关二维甚至更高维区域的查询与更新的问题都可以用线段树嵌套来实现。
树套树下篇:树套树之线段树套平衡树

你可能感兴趣的:(树套树,树,POJ)