文章目录

  • 问题
  • 解析
    • 单点修改
  • 询问
  • 完整代码
  • 标记永久化
    • 代码

所谓二维线段树,就是有两个维度的线段树

(逃)

问题

给出一个矩形
要求支持以下操作:

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;
}

模板:二维线段树(线段树套线段树)相关推荐

  1. BZOJ.4553.[HEOI2016TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j)if(a[j ...

  2. 2019南昌网络赛  I. Yukino With Subinterval 树状数组套线段树

    I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...

  3. H - Hello Ms. Ze(树状数组套主席树,线段树上二分)

    H - Hello Ms. Ze 给定nnn种不同的材料,第iii种材料有aia_iai​个,有mmm个操作,操作分为两类: 把第xxx种材料修改为yyy个, 只用[l,r][l, r][l,r]区间 ...

  4. 金蝶K3Cloud 5.0 套打设计模板 二维码

    金蝶K3Cloud 5.0 套打设计模板 二维码 找了很久没找到,做个记录 条形码-属性-设置-编码: 另外K3这里的控件不能旋转,唉

  5. BZOJ1146[CTSC2008]网络管理——出栈入栈序+树状数组套主席树

    题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条 ...

  6. zoj 2112 树状数组 套主席树 动态求区间 第k个数

    总算是把动态求区间第k个数的算法看明白了. 在主席树的基础上,如果有修改操作,则要通过套树状数组来实现任意区间求第k小的问题. 刚开始看不明白什么意思,现在有一点理解.树状数组的每个元素是一个线段树, ...

  7. (三叉字典树)二叉树套字典树

    看了道题,是要写个字典树排序的,但又不局限于二十六个字母,于是来了个三叉字典树(二叉树套字典树).最差时间复杂度是trie树的常数倍,空间比trie树省得多了. #include<cstdio& ...

  8. ICPC 徐州 H Yuuki and a problem (树状数组套主席树)

    Yuuki and a problem 先不管第一问的修改操作,考虑如何达到第二问的查询操作, 题目要我们给出一个区间[l,r][l, r][l,r]中,不能通过权值+++得到的最小的数字是什么, 假 ...

  9. #279. [SYZOI Round1] 滑稽♂树(树状数组套主席树)

    #279. [SYZOI Round1] 滑稽♂树 子树上的问题,考虑dfsdfsdfs序,第kkk大,可以用主席树嘛,支持修改,那就树状数组上套主席树,参考P4175 [CTSC2008]网络管理( ...

  10. 【BZOJ1901】Dynamic Rankings,树状数组套主席树

    Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门(权限) 题面 1901: Zju2112 Dynamic Rankings Time Limit: 10 Se ...

最新文章

  1. imx6背光驱动调试
  2. 2021暑假每日一题 【week6 完结】
  3. C# 调用SQL的存储过程的接口及实现
  4. php操作mysql的封装类_PHP封装的mysqli数据库操作类示例
  5. 计算机二级通app打不开,计算机二级通app
  6. mac 硬盘未推出 硬盘无法读取_在Mac上(正确的)格式化U盘
  7. JQuery AJAX 加载 HTML代码“lt”形式的。怎么解析成形式,并且把img解析成图片输出到浏览器中。...
  8. Android WindowManager简析
  9. Linux zlog日志打印
  10. python 开源cms内容管理系统_八大CMS内容管理系统推荐
  11. 数据清洗 Chapter01 | 数据清洗概况
  12. 华为软件测试笔试真题之变态逻辑推理题【二】华为爆火面试题
  13. html中楷书的格式,中国书法的书写格式
  14. 买二手苹果macbook被骗真实经历
  15. linux yum仓库命令,linux 自定义yum仓库、repo文件 yum命令
  16. 微信小程序 订单倒计时
  17. drippingblues-靶机渗透
  18. 云计算系统测试之技术概念
  19. ActiviteMQ 对于Consumers pending的限制问题
  20. hgame 2022 PWN 部分题目 Writeup

热门文章

  1. 输出毫秒_自学单片机第十三篇上:单点输出
  2. java号段_JAVA手机号正则(多号段)
  3. linux怎么杀死线程c语言,教程-linux下c语言编程 第一弹-线程的使用
  4. saiku 连接 MySQL_Saiku连接mysql数据库(二)
  5. Linux中append函数的用法,linux C代码 open函数参数:O_APPEND问题求助
  6. 常用计算机二级函数,计算机二级MS office常用函数
  7. 北航计算机学院有河南的,北航计划在豫招生165人 河南多所高职公布预录名单...
  8. 数据结构——模式匹配kmp算法
  9. C++ 学习之旅(2)——链接器Linker
  10. [SpringSecurity]web权限方案_用户注销