C80 二维线段树+标记永久化 区修+区查 P3437 [POI2006] TET-Tetris 3D
视频链接:266 二维线段树+标记永久化 区修+区查 P3437 [POI2006] TET-Tetris 3D_哔哩哔哩_bilibili
Luogu P3437 [POI2006] TET-Tetris 3D
// 二维线段树+标记永久化 区修+区查 空间:O(D*4*D*4) 时间:O(N*logD*logS) #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define ls u<<1 #define rs u<<1|1 #define mid (l+r>>1) const int M=4005; int D,S,N; struct segY{ //内树 int mx[M],tag[M]; //区间最值,永久标记 void change(int u,int l,int r,int y1,int y2,int h){ //内修 mx[u]=max(mx[u],h); //经过则更新mx if(y1<=l&&r<=y2){tag[u]=max(tag[u],h);return;}//覆盖则更新tag if(y1<=mid) change(ls,l,mid,y1,y2,h); if(y2>mid) change(rs,mid+1,r,y1,y2,h); } int query(int u,int l,int r,int y1,int y2){ //内查 if(y1<=l&&r<=y2) return mx[u]; //覆盖则返回mx int ans=tag[u]; //先取tag,再裂开找 if(y1<=mid) ans=max(ans,query(ls,l,mid,y1,y2)); if(y2>mid) ans=max(ans,query(rs,mid+1,r,y1,y2)); return ans; } }mx[M],tag[M]; //外树每个节点维护两颗内树mx,tag void change(int u,int l,int r,int x1,int x2,int y1,int y2,int h){ //外修 mx[u].change(1,1,S,y1,y2,h); //经过则入内树mx if(x1<=l&&r<=x2){tag[u].change(1,1,S,y1,y2,h);return;}//覆盖则入内树tag if(x1<=mid) change(ls,l,mid,x1,x2,y1,y2,h); if(x2>mid) change(rs,mid+1,r,x1,x2,y1,y2,h); } int query(int u,int l,int r,int x1,int x2,int y1,int y2){ //外查 if(x1<=l&&r<=x2) return mx[u].query(1,1,S,y1,y2); //覆盖则入内树mx int ans=tag[u].query(1,1,S,y1,y2); //先入内树tag,再裂开找 if(x1<=mid) ans=max(ans,query(ls,l,mid,x1,x2,y1,y2)); if(x2>mid) ans=max(ans,query(rs,mid+1,r,x1,x2,y1,y2)); return ans; } int main(){ scanf("%d%d%d",&D,&S,&N); int d,s,h,x,y; while(N--){ scanf("%d%d%d%d%d",&d,&s,&h,&x,&y),++x,++y; //偏移 h+=query(1,1,D,x,x+d-1,y,y+s-1); //累计当前区间高度 change(1,1,D,x,x+d-1,y,y+s-1,h); //更新当前区间高度 } printf("%d\n",mx[1].mx[1]); }
// 二维线段树+标记永久化+动态开点 区修+区查 时空复杂度:O(N*logD*logS) #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define ls u<<1 #define rs u<<1|1 #define mid (l+r>>1) const int M=2e6+5; int D,S,N; struct segY{ //内树 int tot,lc[M],rc[M],mx[M],tag[M]; //mx区间最值,tag永久标记 void change(int& u,int l,int r,int y1,int y2,int h){ //内修 if(!u) u=++tot; //内树开点 mx[u]=max(mx[u],h); //经过则更新mx if(y1<=l&&r<=y2){tag[u]=max(tag[u],h);return;} //覆盖则更新tag if(y1<=mid) change(lc[u],l,mid,y1,y2,h); if(y2>mid) change(rc[u],mid+1,r,y1,y2,h); } int query(int u,int l,int r,int y1,int y2){ //内查 if(y1<=l&&r<=y2) return mx[u]; //覆盖则返回mx int ans=tag[u]; //先取tag,再裂开找 if(y1<=mid) ans=max(ans,query(lc[u],l,mid,y1,y2)); if(y2>mid) ans=max(ans,query(rc[u],mid+1,r,y1,y2)); return ans; } }Y; int mx[4005],tag[4005]; //外树的每个节点维护两颗内树mx,tag void change(int u,int l,int r,int x1,int x2,int y1,int y2,int h){ //外修 Y.change(mx[u],1,S,y1,y2,h); //经过则入内树mx if(x1<=l&&r<=x2){Y.change(tag[u],1,S,y1,y2,h);return;}//覆盖则入内树tag if(x1<=mid) change(ls,l,mid,x1,x2,y1,y2,h); if(x2>mid) change(rs,mid+1,r,x1,x2,y1,y2,h); } int query(int u,int l,int r,int x1,int x2,int y1,int y2){ //外查 if(x1<=l&&r<=x2) return Y.query(mx[u],1,S,y1,y2); //覆盖则入内树mx int ans=Y.query(tag[u],1,S,y1,y2); //先入内树tag,再裂开找 if(x1<=mid) ans=max(ans,query(ls,l,mid,x1,x2,y1,y2)); if(x2>mid) ans=max(ans,query(rs,mid+1,r,x1,x2,y1,y2)); return ans; } int main(){ scanf("%d%d%d",&D,&S,&N); int d,s,h,x,y; while(N--){ scanf("%d%d%d%d%d",&d,&s,&h,&x,&y),++x,++y; //偏移 h+=query(1,1,D,x,x+d-1,y,y+s-1); //累计当前区间高度 change(1,1,D,x,x+d-1,y,y+s-1,h); //更新当前区间高度 } printf("%d\n",query(1,1,D,1,D,1,S)); }
区修:把矩形区域{(x1,y1),(x2,y2)}的0改为1,1改为0
点查:查询某一个点的值是0还是1
思路:只对覆盖区间做翻转修改,不用标记。
查询时,累计经过区间的1的个数,即目标点被修改的次数 num。
若 num 为奇数则输出 1,为偶数则输出 0。
// 二维线段树 区修+点查 O(X*T*logN*logN) #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define N 1005 #define uls u<<1 #define urs u<<1|1 #define vls v<<1 #define vrs v<<1|1 #define mid ((l+r)>>1) bool val[N<<2][N<<2]; int X,n,T,num,x1,x2,y1,y2; void changeY(int u,int v,int l,int r){ //内修 if(y1<=l && r<=y2){ //内层覆盖即修改 val[u][v]=!val[u][v]; return; } if(y1<=mid) changeY(u,vls,l,mid); if(y2>mid) changeY(u,vrs,mid+1,r); } void changeX(int u,int l,int r){ //外修 if(x1<=l && r<=x2){ //外层覆盖即入内 changeY(u,1,1,n); return; } if(x1<=mid) changeX(uls,l,mid); if(x2>mid) changeX(urs,mid+1,r); } void queryY(int u,int v,int l,int r){ //内查 if(val[u][v]) num++; //内层经过节点有标记则累计 if(l==r) return; if(y1<=mid) queryY(u,vls,l,mid); else queryY(u,vrs,mid+1,r); } void queryX(int u,int l,int r){ //外查 queryY(u,1,1,n); //外层经过节点均入内 if(l==r) return; if(x1<=mid) queryX(uls,l,mid); else queryX(urs,mid+1,r); } int main(){ char op[2]; while(~scanf("%d",&X)) while(X--){ memset(val,0,sizeof(val)); scanf("%d%d",&n,&T); for(int i=0;i<T;i++){ scanf("%s%d%d",&op,&x1,&y1); if(*op=='C'){ scanf("%d%d",&x2,&y2); changeX(1,1,n); } else{ num=0; queryX(1,1,n); if(num&1) printf("1\n"); else printf("0\n"); } } if(X) printf("\n"); } }
分类:
C 数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!