HDU---4819:Mosaic【二维线段树】

题意:

给定一个N*N的矩阵,每个格子都有一个数,再给出Q个询问,每次询问以(x,y)为中心的边长为L的正方形矩阵中的最大值和最小值,并修改(x,y)的值为(MAX+MIN)/2

分析:

1)四叉树

对于查询矩阵的最值+修改问题,考虑二维线段树,参照一维线段树的写法:每次将区间二分成两个子区间,对应矩阵应对X,Y同时二分,也就是4个子矩阵,即左上、右上、左下,右下四部分,所以要建一颗四叉树,如果当前矩阵为一维时,只需二分成两个子区间,如下图所示:

 归纳总结得出节点X的儿子节点为:X*4-2+a(0=<a<=3,根节点为1),其他就和一维线段树一样了

代码:

#include 

using namespace std;
const int maxn = 1e3+13;
int n,m,x,y,L,T,q,a[maxn][maxn];
struct tree{
    int x1,y1,x2,y2;
    int MAX,MIN;
}tr[maxn*maxn*4];
inline int son(int p,int x){
    return p*4-2+x;
}
void pushup1(int x){                  //四叉树的pushup
    tr[x].MAX = max(tr[son(x,0)].MAX,tr[son(x,1)].MAX);
    tr[x].MIN = min(tr[son(x,0)].MIN,tr[son(x,1)].MIN);
    for(int i = 2;i < 4; ++i){
        tr[x].MAX = max(tr[x].MAX,tr[son(x,i)].MAX);
        tr[x].MIN = min(tr[x].MIN,tr[son(x,i)].MIN);
    }
}
void pushup2(int x){                                 //二叉树的pushup
    tr[x].MAX = max(tr[son(x,0)].MAX,tr[son(x,1)].MAX);
    tr[x].MIN = min(tr[son(x,0)].MIN,tr[son(x,1)].MIN);
}
void build(int X1,int Y1,int X2,int Y2,int index){
    tr[index].x1 = X1,tr[index].x2 = X2;
    tr[index].y1 = Y1,tr[index].y2 = Y2;
    if(X1 == X2 && Y1 == Y2){
        tr[index].MAX = a[X1][Y1];
        tr[index].MIN = a[X1][Y1];
        return ;
    }
    int midx = (X1+X2) >> 1;
    int midy = (Y1+Y2) >> 1;
    if(X1 == X2){
        build(X1,Y1,X2,midy,son(index,0));
        build(X1,midy+1,X2,Y2,son(index,1));
        pushup2(index);
    }
    else if(Y1 == Y2){
        build(X1,Y1,midx,Y2,son(index,0));
        build(midx+1,Y1,X2,Y2,son(index,1));
        pushup2(index);
    }
    else{                                         //四个子矩阵
        build(X1,Y1,midx,midy,son(index,0));
        build(midx+1,Y1,X2,midy,son(index,1));
        build(X1,midy+1,midx,Y2,son(index,2));
        build(midx+1,midy+1,X2,Y2,son(index,3));
        pushup1(index);
    }
}
int A,B;
void query(int X1,int Y1,int X2,int Y2,int x){
    if(tr[x].x1>X2||tr[x].x2Y2||tr[x].y2=X1&&tr[x].x2<=X2&&tr[x].y1>=Y1&&tr[x].y2<=Y2){
        A = max(A,tr[x].MAX);
        B = min(B,tr[x].MIN);
        return ;
    }
    if(tr[x].x1 == tr[x].x2 || tr[x].y1 == tr[x].y2){
        query(X1,Y1,X2,Y2,son(x,0));
        query(X1,Y1,X2,Y2,son(x,1));
    }
    else{
        query(X1,Y1,X2,Y2,son(x,0));
        query(X1,Y1,X2,Y2,son(x,1));
        query(X1,Y1,X2,Y2,son(x,2));
        query(X1,Y1,X2,Y2,son(x,3));
    }
}
void updata(int X1,int Y1,int X2,int Y2,int x,int val){
    if(tr[x].x1>X2||tr[x].x2Y2||tr[x].y2=X1&&tr[x].x2<=X2&&tr[x].y1>=Y1&&tr[x].y2<=Y2){
        tr[x].MAX = val;
        tr[x].MIN = val;
        return ;
    }
    if(tr[x].x1 == tr[x].x2 || tr[x].y1 == tr[x].y2){
        updata(X1,Y1,X2,Y2,son(x,0),val);
        updata(X1,Y1,X2,Y2,son(x,1),val);
        pushup2(x);
    }
    else{
        updata(X1,Y1,X2,Y2,son(x,0),val);
        updata(X1,Y1,X2,Y2,son(x,1),val);
        updata(X1,Y1,X2,Y2,son(x,2),val);
        updata(X1,Y1,X2,Y2,son(x,3),val);
        pushup1(x);
    }
}
int main(){
    scanf("%d",&T);
    for(int i = 1;i <= T; ++i){
        scanf("%d",&n);
        for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= n; ++j)
        scanf("%d",&a[i][j]);
        build(1,1,n,n,1);
        scanf("%d",&q);
        printf("Case #%d:\n",i);
        int X1,X2,Y1,Y2;
        while(q--){
            scanf("%d %d %d",&x,&y,&L);
            L = (L-1) >> 1;
            X1 = max(1,x-L),Y1 = max(1,y-L);
            X2 = min(n,x+L),Y2 = min(n,y+L);
            A = 0,B = 1e9+19;
            query(X1,Y1,X2,Y2,1);
            updata(x,y,x,y,1,(A+B)>>1);
            printf("%d\n",(A+B)>>1);
        }
    }
    return 0;
} 

2)树套树

先对X轴建一颗一维线段树,再在其每个节点下建一颗Y轴的一维线段树,那么X树的每个节点维护:x轴上[xl,xr],整个y轴的矩阵的信息,考虑如何向上维护信息(下图以纵为x)

(1)如果当前x节点为x树上的叶子节点,如X2 和 X3,那么它的y树就和普通的一维线段树一样维护

(2)如果当前x节点不是x树上的叶子节点,如X1,那么其y树的叶子节点是由它的儿子节点的y树的叶子节点合并而来

HDU---4819:Mosaic【二维线段树】_第1张图片

代码:

#include 

using namespace std;
const int maxn = 805;
int MAX[maxn<<2][maxn<<2],MIN[maxn<<2][maxn<<2];  //第一维表示x树上节点的编号
int T,n,Q,a[maxn][maxn],xl,yl,xr,yr,x,y,L,newv,maxv,minv;
inline void pushupx(int x,int y){          //x树上的非叶子节点的y树的叶子节点通过儿子节点合并而来
    MAX[x][y] = max(MAX[x<<1][y],MAX[x<<1|1][y]);
    MIN[x][y] = min(MIN[x<<1][y],MIN[x<<1|1][y]);
}
inline void pushupy(int x,int y){
    MAX[x][y] = max(MAX[x][y<<1],MAX[x][y<<1|1]);
    MIN[x][y] = min(MIN[x][y<<1],MIN[x][y<<1|1]);
}
void buildy(int l,int r,int y,int x,int k){     //k标记是不是儿子节点
    if(l == r){
        if(k) MAX[x][y] = MIN[x][y] = a[k][r];
        else pushupx(x,y); 
        return ;
    }
    int mid = (l+r) >> 1;
    buildy(l,mid,y<<1,x,k);
    buildy(mid+1,r,y<<1|1,x,k);
    pushupy(x,y);
}
void buildx(int l,int r,int x){
    if(l == r){
        buildy(1,n,1,x,r);
        return ;
    }
    int mid = (l+r) >> 1;
    buildx(l,mid,x<<1);
    buildx(mid+1,r,x<<1|1);
    buildy(1,n,1,x,0);
}
void updatay(int l,int r,int y,int x,int k){
    if(l > yr || r < yl) return;
    if(l >= yl && r <= yr){
        if(k) MAX[x][y] = MIN[x][y] = newv;
        else pushupx(x,y);
        return ;
    }
    int mid = (l+r) >> 1;
    updatay(l,mid,y<<1,x,k);
    updatay(mid+1,r,y<<1|1,x,k);
    pushupy(x,y);
}
void updatax(int l,int r,int x){
    if(l > xr || r < xl) return;
    if(l >= xl && r <= xr){
        updatay(1,n,1,x,r);
        return ;
    }
    int mid = (l+r) >> 1;
    updatax(l,mid,x<<1);
    updatax(mid+1,r,x<<1|1);
    updatay(1,n,1,x,0);
}
void queryy(int l,int r,int y,int x){
    if(l > yr || r < yl) return ;
    if( l >= yl && r <= yr){
        maxv = max(maxv,MAX[x][y]);
        minv = min(minv,MIN[x][y]);
        return ;
    }
    int mid = (l+r) >> 1;
    queryy(l,mid,y<<1,x);
    queryy(mid+1,r,y<<1|1,x);
}
void queryx(int l,int r,int x){
    if(l > xr || r < xl) return ;
    if(l >= xl && r <= xr){
        queryy(1,n,1,x); 
        return ;
    }
    int mid = (l+r) >> 1;
    queryx(l,mid,x<<1);
    queryx(mid+1,r,x<<1|1);
}
int main(){
    scanf("%d",&T);
    for(int i = 1;i <= T; ++i){
        scanf("%d",&n);
        for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= n; ++j)
        scanf("%d",&a[i][j]);
        buildx(1,n,1);
        scanf("%d",&Q);
        printf("Case #%d:\n",i);
        while(Q--){
            scanf("%d %d %d",&x,&y,&L);
            L = (L-1) >> 1;
            xl = max(1,x-L),yl = max(1,y-L);
            xr = min(n,x+L),yr = min(n,y+L);
            maxv = 0,minv = 1e9;
            queryx(1,n,1);
            newv = (maxv + minv) >> 1;
            xl = xr = x;yl = yr = y;
            updatax(1,n,1);
            printf("%d\n",newv);
        } 
    }
    return 0;
} 

两种方法单次操作的时间复杂度都是O(log^2),但第一种最差可以退化到O(n),此题第二种写法就比第一种快了近5倍

你可能感兴趣的:(数据结构----线段树,二维线段树)