文章目录
- 问题
- 解析
- 单点修改
- 询问
- 完整代码
- 标记永久化
- 代码
所谓二维线段树,就是有两个维度的线段树
(逃)
问题
给出一个矩形
要求支持以下操作:
1.询问一个子矩形的最值
2.修改某一个单点的值
解析
使用线段树套线段树,来解决二维动态问题
注意这个东西只能支持单点修改,区间查询
区间改直接死翘翘
对于x轴开一个线段树
每个结点对应一个y方向[1,n]的长条
时空复杂度qlogn2qlogn^2qlogn2
在求和问题时不妨用map套树状树组
就可以支持区间修改了
而且代码号写许多
但缺陷是时间复杂度由于套map变成了3log
跑得飞慢qwq
单点修改
由于这个矩形可能很大(比如长宽1e5级别)
我们可能无法把整棵线段树存下来
所以使用x方向直接开,y方向动态开点
为什么x方向不动态开点?因为那实在是太不好写了…
那如果长宽1e9级别怎么办?你去死吧
注意x方向修改完往上递归的时候要递归到对应的y方向的叶子更新信息
具体实现的时候我开了一个map,mpi,jmp_{i,j}mpi,j记录x方向编号为i的树的y方向上j号叶子结点的编号(如果没看明白看代码就清楚了)
这样时空复杂度还是对的
注意map赋值的位置,不然可能使你凭空变成三个log
map<int,int>mp[N<<2];
void change(int line,int &k,int l,int r,int p,int v,int flag=0){if(!k){k=New();if(l==r) mp[line][p]=k;}if(l==r){if(flag==0) tr[k].mx=tr[k].mn=v;else{int L=mp[line<<1][p],R=mp[line<<1|1][p];tr[k].mx=max(tr[L].mx,tr[R].mx);tr[k].mn=min(tr[L].mn,tr[R].mn);}return;}if(p<=mid) change(line,tr[k].ls,l,mid,p,v,flag);else change(line,tr[k].rs,mid+1,r,p,v,flag);pushup(k);return;
}
void Add(int k,int l,int r,int x,int y,int v){if(l==r){change(k,rt[k],1,n,y,v,0);return;}if(x<=mid) Add(k<<1,l,mid,x,y,v);else Add(k<<1|1,mid+1,r,x,y,v);change(k,rt[k],1,n,y,v,1);return;
}
询问
二维线段树主要难写的地方其实就是修改
询问相比之下就比较常规了
直接递归到对应的树上取答案即可
这里贴一个取max的
int askmx(int k,int l,int r,int x,int y){if(!k) return -2e9;if(x<=l&&r<=y){return tr[k].mx;}int res=-2e9;if(x<=mid) Max(res,askmx(tr[k].ls,l,mid,x,y));if(y>mid) Max(res,askmx(tr[k].rs,mid+1,r,x,y));return res;
}
int Querymx(int k,int l,int r,int x1,int y1,int x2,int y2){if(x1<=l&&r<=x2){return askmx(rt[k],1,n,y1,y2);}int res=-2e9;if(x1<=mid) Max(res,Querymx(k<<1,l,mid,x1,y1,x2,y2));if(x2>mid) Max(res,Querymx(k<<1|1,mid+1,r,x1,y1,x2,y2));return res;
}
完整代码
板子题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
const int N=850;
const int M=3e6+100;
const int mod=998244353;
inline ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
int n,m;inline void Max(int &x,int y){x=max(x,y);}
inline void Min(int &x,int y){x=min(x,y);}
int rt[N<<2];
struct tree{int mx,mn,ls,rs;
}tr[M];
int tot;
#define mid ((l+r)>>1)
inline int New(){++tot;tr[tot].mx=-2e9;tr[tot].mn=2e9;tr[tot].ls=tr[tot].rs=0;return tot;
}
inline void pushup(int x){tr[x].mx=max(tr[tr[x].ls].mx,tr[tr[x].rs].mx);tr[x].mn=min(tr[tr[x].ls].mn,tr[tr[x].rs].mn);return;
}
map<int,int>mp[N<<2];
void change(int line,int &k,int l,int r,int p,int v,int flag=0){if(!k){k=New();if(l==r) mp[line][p]=k;}if(l==r){if(flag==0) tr[k].mx=tr[k].mn=v;else{int L=mp[line<<1][p],R=mp[line<<1|1][p];tr[k].mx=max(tr[L].mx,tr[R].mx);tr[k].mn=min(tr[L].mn,tr[R].mn);}return;}if(p<=mid) change(line,tr[k].ls,l,mid,p,v,flag);else change(line,tr[k].rs,mid+1,r,p,v,flag);pushup(k);return;
}
void Add(int k,int l,int r,int x,int y,int v){if(l==r){change(k,rt[k],1,n,y,v,0);return;}if(x<=mid) Add(k<<1,l,mid,x,y,v);else Add(k<<1|1,mid+1,r,x,y,v);change(k,rt[k],1,n,y,v,1);return;
}
int askmn(int k,int l,int r,int x,int y){if(!k) return 2e9;if(x<=l&&r<=y){return tr[k].mn;}int res=2e9;if(x<=mid) Min(res,askmn(tr[k].ls,l,mid,x,y));if(y>mid) Min(res,askmn(tr[k].rs,mid+1,r,x,y));return res;
}
int askmx(int k,int l,int r,int x,int y){if(!k) return -2e9;if(x<=l&&r<=y){return tr[k].mx;}int res=-2e9;if(x<=mid) Max(res,askmx(tr[k].ls,l,mid,x,y));if(y>mid) Max(res,askmx(tr[k].rs,mid+1,r,x,y));return res;
}
int Querymx(int k,int l,int r,int x1,int y1,int x2,int y2){if(x1<=l&&r<=x2){return askmx(rt[k],1,n,y1,y2);}int res=-2e9;if(x1<=mid) Max(res,Querymx(k<<1,l,mid,x1,y1,x2,y2));if(x2>mid) Max(res,Querymx(k<<1|1,mid+1,r,x1,y1,x2,y2));return res;
}
int Querymn(int k,int l,int r,int x1,int y1,int x2,int y2){if(x1<=l&&r<=x2){return askmn(rt[k],1,n,y1,y2);}int res=2e9;if(x1<=mid) Min(res,Querymn(k<<1,l,mid,x1,y1,x2,y2));if(x2>mid) Min(res,Querymn(k<<1|1,mid+1,r,x1,y1,x2,y2));return res;
}
int main(){tr[0].mx=-2e9;tr[0].mn=2e9;scanf("%d",&n);//if(o==3) break;memset(rt,0,sizeof(rt));tot=0;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){int x=read();Add(1,1,n,i,j,x);}}m=read();for(int i=1;i<=m;i++){char c;scanf(" %c",&c);if(c=='c'){int x=read(),y=read(),v=read();Add(1,1,n,x,y,v);}else{int x1=read(),y1=read(),x2=read(),y2=read();int mx=Querymx(1,1,n,x1,y1,x2,y2),mn=Querymn(1,1,n,x1,y1,x2,y2);printf("%d %d\n",mx,mn);}}return 0;
}
/*
3 1
3 1 33 2
1 1 2
3 1 3
*/
标记永久化
尽管二维线段树无法实现区间赋值
但是在一些特殊的情况下可以通过骚操作使其具有区间赋值的功能
那就是这个!标记永久化
大概的思路就是x和y方向上都建两棵树,一棵是区间的最大值mx,一棵存该区间整体赋过的最大值tag
修改的时候沿途对所有的mx更新,并对最终子区间的tag更新
询问的时候,如果是子区间,直接返回mx,否则先把res赋值成tag,再递归尝试更新这个res
确实是挺妙的
但这个东西是有局限性的
就像它的名字一样,这个标记无法撤销
比如维护最大值的时候,如果可以把值改小,就炸了
更具体的细节看代码吧
代码
板子
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(a,b) fprintf(stderr,a,b)
const int N=1030;
const int mod=1e9+7;
inline ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
int n,m,k;
struct node{int mx,tag,ls,rs;
}tr[N*N*15];
int mx[N<<2],tag[N<<2],tot;
#define mid ((l+r)>>1)
int ask(int k,int l,int r,int x,int y){if(!k) return 0;if(x<=l&&r<=y){//printf("ask:k=%d (%d %d) (%d %d) mx=%d\n",k,l,r,x,y,tr[k].mx);return tr[k].mx;}int res=tr[k].tag;//printf("ask:k=%d (%d %d) (%d %d) tag=%d\n",k,l,r,x,y,tr[k].tag);if(x<=mid) res=max(res,ask(tr[k].ls,l,mid,x,y));if(y>mid) res=max(res,ask(tr[k].rs,mid+1,r,x,y));return res;
}
int Query(int k,int l,int r,int x1,int y1,int x2,int y2){if(x1<=l&&r<=x2){int o=ask(mx[k],1,m,y1,y2);//printf("Query:k=%d (%d %d) [(%d %d),(%d %d)] mx=%d\n",k,l,r,x1,y1,x2,y2,o);return o;}int res=ask(tag[k],1,m,y1,y2);//printf("Query:k=%d (%d %d) [(%d %d),(%d %d)] tag=%d\n",k,l,r,x1,y1,x2,y2,res);if(x1<=mid) res=max(res,Query(k<<1,l,mid,x1,y1,x2,y2));if(x2>mid) res=max(res,Query(k<<1|1,mid+1,r,x1,y1,x2,y2));return res;
}
void change(int &k,int l,int r,int x,int y,int v){if(!k) k=++tot;tr[k].mx=max(tr[k].mx,v);if(x<=l&&r<=y){//printf("change:k=%d (%d %d) (%d %d) tag:v=%d\n",k,l,r,x,y,v);tr[k].tag=max(tr[k].tag,v);return;}if(x<=mid) change(tr[k].ls,l,mid,x,y,v);if(y>mid) change(tr[k].rs,mid+1,r,x,y,v);return;
}
void Add(int k,int l,int r,int x1,int y1,int x2,int y2,int v){change(mx[k],1,m,y1,y2,v);if(x1<=l&&r<=x2){change(tag[k],1,m,y1,y2,v);return;}if(x1<=mid) Add(k<<1,l,mid,x1,y1,x2,y2,v);if(x2>mid) Add(k<<1|1,mid+1,r,x1,y1,x2,y2,v);return;
}
int main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endif//printf("%d\n",sizeof(tr)/1024/1024);n=read();m=read();k=read();++n;++m;for(int i=1;i<=k;i++){int l=read(),w=read(),h=read(),x=read(),y=read();x++;y++;int x1=x,y1=y,x2=x+l-1,y2=y+w-1;int mx=Query(1,1,n,x1,y1,x2,y2);Add(1,1,n,x1,y1,x2,y2,mx+h);//printf("(%d %d) (%d %d) mx=%d -> %d\n",x1,y1,x2,y2,mx,mx+h);}printf("%d\n",Query(1,1,n,1,1,n,m));return 0;
}