博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基本线段树模板(建树、点/区间修改、查询)
阅读量:6679 次
发布时间:2019-06-25

本文共 4738 字,大约阅读时间需要 15 分钟。

线段树主要用于区间记录信息(如区间和、最大最小值等),首先是建树:

 

这里以求和为例:

1 const int MAXM=50000;          //定义 MAXM 为线段最大长度 2  3 int a[MAXM+5],st[(MAXM<<2)+5];    // a 数组为 main 函数中读入的内容,st 数组为需要查询的数的信息(如和、最值等),树的空间大小为线段最大长度的四倍 4  5 void build(int o,int l,int r){    //传入的参数为 o:当前需要建立的结点;l:当前需要建立的左端点;r:当前需要建立的右端点 6     if(l==r)st[o]=a[l];      //当左端点等于右端点即建立叶子结点时,直接给数组信息赋值 7     else{ 8         int m=l+((r-l)>>1);      // m 为中间点,左儿子结点为 [l,m] ,右儿子结点为 [m+1,r]; 9         build(o<<1,l,m);        //构建左儿子结点10         build((o<<1)|1,m+1,r);     //构建右儿子结点11         st[o]=st[o<<1]+st[(o<<1)|1];  //递归返回时用儿子结点更新父节点,此处可进行更新最大值、最小值、区间和等操作12     }13 }14 15 {                       //在 main 函数中的语句16         build(1,1,n);17 }

 

然后是比较简单的单点修改以及区间查询操作:

单点修改:

1 void update(int o,int l,int r,int ind,int ans){  //o、l、r为当前更新到的结点、左右端点,ind为需要修改的叶子结点左端点,ans为需要修改成的值; 2     if(l==r){                      //若当前更新点的左右端点相等即到叶子结点时,直接更新信息并返回 3         st[o]=ans; 4         return; 5     } 6     int m=l+((r-l)>>1); 7     if(ind<=m){                      //若需要更新的叶子结点在当前结点的左儿子结点的范围内,则递归更新左儿子结点,否则更新右儿子结点 8         update(o<<1,l,m,ind,ans); 9     }10     else{11         update((o<<1)|1,m+1,r,ind,ans);12     }13     st[o]=max(st[o<<1],st[(o<<1)|1]);//递归回之后用儿子结点更新父节点(此处是区间最大值)14 }15 16 {                               //在main函数中的语句17         update(1,1,n,ind,ans);18 }

对应单点修改的区间查询:

1 int query(int o,int l,int r,int ql,int qr){      //ql、qr为需要查询的区间左右端点 2     if(ql>r||qr
=r) return st[o];        //若当前结点的区间被需要查询的区间覆盖,则返回当前结点的信息 4 int m=l+((r-l)>>1); 5 int p1=query(o<<1,l,m,ql,qr),p2=query((o<<1)|1,m+1,r,ql,qr);  //p1为查询左儿子结点得到的信息,p2为查询右儿子结点得到的信息 6 return max(p1,p2);    //综合两个儿子结点的信息并返回 7 } 8 9 {    //main函数中的语句10 printf("%d\n",query(1,1,n,a,b));11 }

 

然后是线段数的区间修改以及相应的查询:

区间修改用到了lazy的思想,即当一个区间需要更新时,只递归更新到那一层结点,并将其下层结点所需要更新的信息保存在数组中,然后返回,只有当下次遍历到那个结点(更新过程中或查询过程中),才将那个结点的修改信息传递下去,这样就避免了区间修改的每个值的修改

区间修改(包括区间加值和区间赋值)及相应查询:

区间加值:

1 void pushup(int o){          //pushup函数,该函数本身是将当前结点用左右子节点的信息更新,此处求区间和,用于update中将结点信息传递完返回后更新父节点 2     st[o]=st[o<<1]+st[o<<1|1]; 3 } 4    5 void pushdown(int o,int l,int r){  //pushdown函数,将o结点的信息传递到左右子节点上 6     if(add[o]){             //当父节点有更新信息时才向下传递信息 7         add[o<<1]+=add[o];      //左右儿子结点均加上父节点的更新值 8         add[o<<1|1]+=add[o]; 9         int m=l+((r-l)>>1);10         st[o<<1]+=add[o]*(m-l+1);  //左右儿子结点均按照需要加的值总和更新结点信息11         st[o<<1|1]+=add[o]*(r-m);12         add[o]=0;                //信息传递完之后就可以将父节点的更新信息删除13     }14 }15  16 void update(int o,int l,int r,int ql,int qr,int addv){  //ql、qr为需要更新的区间左右端点,addv为需要增加的值17     if(ql<=l&&qr>=r){                      //与单点更新一样,当当前结点被需要更新的区间覆盖时18         add[o]+=addv;                      //更新该结点的所需更新信息19         st[o]+=addv*(r-l+1);                //更新该结点信息20         return;                    //根据lazy思想,由于不需要遍历到下层结点,因此不需要继续向下更新,直接返回21     }22     23     pushdown(o,l,r);                  //将当前结点的所需更新信息传递到下一层(其左右儿子结点)24     int m=l+((r-l)>>1);25     if(ql<=m)update(o<<1,l,m,ql,qr,addv);     //当需更新区间在当前结点的左儿子结点内,则更新左儿子结点26     if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,addv);   //当需更新区间在当前结点的右儿子结点内,则更新右儿子结点27     pushup(o);                  //递归回上层时一步一步更新回父节点28 }29 30 ll query(int o,int l,int r,int ql,int qr){    //ql、qr为需要查询的区间31     if(ql<=l&&qr>=r) return st[o];      //若当前结点覆盖区间即为需要查询的区间,则直接返回当前结点的信息32     pushdown(o,l,r);                  //将当前结点的更新信息传递给其左右子节点33     int m=l+((r-l)>>1);34     ll ans=0;                      //所需查询的结果35     if(ql<=m)ans+=query(o<<1,l,m,ql,qr);     //若所需查询的区间与当前结点的左子节点有交集,则结果加上查询其左子节点的结果36     if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr); //若所需查询的区间与当前结点的右子节点有交集,则结果加上查询其右子节点的结果37    return ans; 38 }

 

区间改值(其实只有pushdow函数和update中修改部分与区间加值不同):

 

1  void pushup(int o){ 2      st[o]=st[o<<1]+st[o<<1|1]; 3  } 4   5  void pushdown(int o,int l,int r){  //pushdown和区间加值不同,改值时修改结点信息只需要对修改后的信息求和即可,不用加上原信息 6      if(change[o]){ 7          int c=change[o]; 8          change[o<<1]=c; 9          change[o<<1|1]=c;10          int m=l+((r-l)>>1);11          st[o<<1]=(m-l+1)*c;12          st[o<<1|1]=(r-m)*c;13          change[o]=0;14      }15  }16  17  void update(int o,int l,int r,int ql,int qr,int c){18      if(ql<=l&&qr>=r){         //同样更新结点信息和区间加值不同19          change[o]=c;20          st[o]=(r-l+1)*c;21          return;22      }23      24      pushdown(o,l,r);25      int m=l+((r-l)>>1);26      if(ql<=m)update(o<<1,l,m,ql,qr,c);27      if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,c);28      pushup(o);29  }30  31  int query(int o,int l,int r,int ql,int qr){32      if(ql<=l&&qr>=r) return st[o];33      pushdown(o,l,r);34      int m=l+((r-l)>>1);35      int ans=0;36      if(ql<=m)ans+=query(o<<1,l,m,ql,qr);37      if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr);38      return ans;39  }

 

转载于:https://www.cnblogs.com/cenariusxz/p/4336043.html

你可能感兴趣的文章
组织炎症水平高的RA患者接受TNF拮抗剂治疗的效果更好
查看>>
[洛谷P3709]大爷的字符串题
查看>>
通过映射关系 动态转义为统一格式的数据 (支持 JSON 和 XML )
查看>>
ajax跨域解决方案(服务端仅限java)
查看>>
Shell 文本处理三剑客之grep
查看>>
如何写出让人看了恶心的代码
查看>>
http状态码
查看>>
好记性不如烂笔杆-android学习笔记<十五> GridView简单用法
查看>>
最短路径
查看>>
表格相关技巧(双击启动事件、取得行号、定义表格的读写属性)
查看>>
ubuntu server vsftpd 虚拟用户及目录
查看>>
GCD多线程使用
查看>>
[转载] 格式化字符串漏洞原理介绍
查看>>
python小项目之微信远程控制
查看>>
Mysql本地安装多实例后启动遇到的问题
查看>>
用 RPM 打包软件,第 1 部分
查看>>
POJ题目(转)
查看>>
js使用闭包时,内部函数是直接访问外部函数的实际变量而非复制一份新变量...
查看>>
P3622 [APIO2007]动物园
查看>>
HBase原理和设计
查看>>