hdu4819 Mosaic 二维线段树 单点更新,区间查询RMQ

如果写二维线段树区间RMQ,不能单点更新的话,那么和咸鱼有什么区别。

所以弄了一个下午,终于把更新弄出来了。。。

这个是这样的,既然是单点更新,一定会更新到最底层,因此先更新到第一维的最底层,在到第二维一直更新到那个点,然后由那个点一直分两个方向更新上来。

这样的话就不要把第二维弄成结构体了,因为要用第一维的根值,不过这样反而更好写。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

typedef long long ll;
const int maxn=1200;
const int INF=1e9+10;

int n,q;
int x,y,z;
int t,A,B;
struct Node
{
    int Min,Max;
};Node tr[maxn<<2][maxn<<2];

void upy(int rt,int rtx)
{
    tr[rtx][rt].Min=min(tr[rtx][rt<<1].Min,tr[rtx][rt<<1|1].Min);
    tr[rtx][rt].Max=max(tr[rtx][rt<<1].Max,tr[rtx][rt<<1|1].Max);
}

void buildy(int l,int r,int rt,int rtx)
{
    if(l==r){
        tr[rtx][rt].Max=-INF;
        tr[rtx][rt].Min=INF;
        return;
    }
    int m=(l+r)>>1;
    buildy(lson,rtx);
    buildy(rson,rtx);
    upy(rt,rtx);
}

void updatey(int p,int c,int l,int r,int rt,int rtx)
{
    if(l==r){
        tr[rtx][rt].Min=tr[rtx][rt].Max=c;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m) updatey(p,c,lson,rtx);
    else updatey(p,c,rson,rtx);
    upy(rt,rtx);
}

int queryMiny(int L,int R,int l,int r,int rt,int rtx)
{
    if(L<=l&&r<=R) return tr[rtx][rt].Min;
    int m=(l+r)>>1;
    int res=INF;
    if(L<=m) res=min(res,queryMiny(L,R,lson,rtx));
    if(R>m) res=min(res,queryMiny(L,R,rson,rtx));
    return res;
}

int queryMaxy(int L,int R,int l,int r,int rt,int rtx)
{
    if(L<=l&&r<=R) return tr[rtx][rt].Max;
    int m=(l+r)>>1;
    int res=-INF;
    if(L<=m) res=max(res,queryMaxy(L,R,lson,rtx));
    if(R>m) res=max(res,queryMaxy(L,R,rson,rtx));
    return res;
}

void buildx(int l,int r,int rt)
{
    buildy(1,n,1,rt);
    if(l==r) return;
    int m=(l+r)>>1;
    buildx(lson);
    buildx(rson);
}

void upx(int p,int rtx,int l,int r,int rt)
{
    tr[rtx][rt].Min=min(tr[rtx<<1][rt].Min,tr[rtx<<1|1][rt].Min);
    tr[rtx][rt].Max=max(tr[rtx<<1][rt].Max,tr[rtx<<1|1][rt].Max);
    if(l==r) return;
    int m=(l+r)>>1;
    if(p<=m) upx(p,rtx,lson);
    else upx(p,rtx,rson);
}

void updatex(int x,int y,int c,int l,int r,int rt)
{
    if(l==r){
        updatey(y,c,1,n,1,rt);
        return;
    }
    int m=(l+r)>>1;
    if(x<=m) updatex(x,y,c,lson);
    else updatex(x,y,c,rson);
    upx(y,rt,1,n,1);
}

int queryMinx(int xL,int xR,int yL,int yR,int l,int r,int rt)
{
    if(xL<=l&&r<=xR) return queryMiny(yL,yR,1,n,1,rt);
    int m=(l+r)>>1;
    int res=INF;
    if(xL<=m) res=min(res,queryMinx(xL,xR,yL,yR,lson));
    if(xR>m) res=min(res,queryMinx(xL,xR,yL,yR,rson));
    return res;
}

int queryMaxx(int xL,int xR,int yL,int yR,int l,int r,int rt)
{
    if(xL<=l&&r<=xR) return queryMaxy(yL,yR,1,n,1,rt);
    int m=(l+r)>>1;
    int res=-INF;
    if(xL<=m) res=max(res,queryMaxx(xL,xR,yL,yR,lson));
    if(xR>m) res=max(res,queryMaxx(xL,xR,yL,yR,rson));
    return res;
}

int main()
{
    freopen("in.txt","r",stdin);
    int T;cin>>T;
    int casen=1;
    while(T--){
        scanf("%d",&n);
        buildx(1,n,1);
        REP(i,1,n) REP(j,1,n) scanf("%d",&x),updatex(i,j,x,1,n,1);
        scanf("%d",&q);
        printf("Case #%d:\n",casen++);
        while(q--){
            scanf("%d%d%d",&x,&y,&z);
            t=(z-1)/2;
            A=queryMinx(x-t,x+t,y-t,y+t,1,n,1);
            B=queryMaxx(x-t,x+t,y-t,y+t,1,n,1);
            updatex(x,y,(A+B)/2,1,n,1);
            printf("%d\n",(A+B)/2);
        }
    }
    return 0;
}
View Code

这样其实不管是区间求和,区间最值,或者区间合并,只要只涉及单点更新不涉及区间更新的,基本没什么难度。

问题在于如何处理更一般区间更新然后区间查询的。

如果是区间更新,单点查询的话,这样只要用一些性质,不把标记传递下去,查询的时候直接路过路径上的结点的时候通过标记顺便更新,这是一个很好用的做法,它有个专业名词叫标记永久化,但不能改变它奇技淫巧的本质。。。

其实先上面第一种做法,设个两个标记感觉还是可以传递的,反正从修改的地方一直分两个方向更新上来,不过写起来感觉略复杂啊。。。以后再弄。。。

现在的重点不是二维线段树,而是树套树。。。

posted @ 2016-03-05 18:30  __560  阅读(305)  评论(0编辑  收藏  举报