hdu4819 Mosaic 二维线段树 单点更新,区间查询RMQ
如果写二维线段树区间RMQ,不能单点更新的话,那么和咸鱼有什么区别。
所以弄了一个下午,终于把更新弄出来了。。。
这个是这样的,既然是单点更新,一定会更新到最底层,因此先更新到第一维的最底层,在到第二维一直更新到那个点,然后由那个点一直分两个方向更新上来。
这样的话就不要把第二维弄成结构体了,因为要用第一维的根值,不过这样反而更好写。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
这样其实不管是区间求和,区间最值,或者区间合并,只要只涉及单点更新不涉及区间更新的,基本没什么难度。
问题在于如何处理更一般区间更新然后区间查询的。
如果是区间更新,单点查询的话,这样只要用一些性质,不把标记传递下去,查询的时候直接路过路径上的结点的时候通过标记顺便更新,这是一个很好用的做法,它有个专业名词叫标记永久化,但不能改变它奇技淫巧的本质。。。
其实先上面第一种做法,设个两个标记感觉还是可以传递的,反正从修改的地方一直分两个方向更新上来,不过写起来感觉略复杂啊。。。以后再弄。。。
现在的重点不是二维线段树,而是树套树。。。
没有AC不了的题,只有不努力的ACMER!