线段树(区间修改)模板题 Luogu 2357 守墓人
众所周知,线段树可以在O( log n)的时间内进行很多修改和查询的操作,应用很广。
线段树,顾名思义,是一个二叉树,但是每个节点,存的不是不是“数”,而是一个“区间”,在百度百科中有非常容易理解的图片,一看就能理解线段树是怎么存在的。
线段树的存储方式和树的相似。
例如:有10 个数,那么,根节点的sum就是这10个数的和,lazy标记表示这十个数都要加上一个数,那么根节点的左二子就存的1—5的sum,lazy只是1—5范围的,右儿子表示6—10的,左二子的左二子是1—3,左二子的右儿子是2—5,右儿子的……以此类推,一直拆分到每个节点表示一个数的为止。
这道模板题主要用到了区间修改、单点修改和区间查询、单点查询(为了偷懒,我将单点修改、查询写成了查询、修改长度为 1 的区间……sorry)。
实现线段树的区间操作需要用到懒标记 lazy 数组,涉及到懒标记的下放(pushdown)和上传(pushup);
题意详见 Luogu 2357 守墓人;
下面在代码中对线段树进行分析:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<algorithm> 7 using namespace std; 8 9 long long num[1000000]; //num数组存的是该节点的区间有多少个数,按理说这个是用不到的, //但我菜 QAQ~ ,迫不得已又开了这个数组,下面会说…… 10 long long a[1000000],sum[1000000],n,f,lazy[1000000],m,l,r,k;// 11 long long ans; //a数组存的是该节点的数…… //sum存的线段树的节点表示的区间的值的和。 //lazy是懒标记,标记这个区间要加的数,因为“懒”,所以用不到时就一边待着…… 12 13 void pushdown(int o){ 14 lazy[o*2+1]+=lazy[o]; 15 lazy[o*2]+=lazy[o]; 16 lazy[o]=0; 17 return; 18 } 19 //下放函数,将该节点的懒标记,下放到左孩子和右孩子,自己的清零。 20 void pushup(int o){ 21 if(o==1) return; 22 int p=o/2; 23 sum[p]=sum[p*2]+sum[p*2+1]+lazy[p*2]*num[p*2]+lazy[p*2+1]*num[p*2+1]; 24 pushup(o/2); 25 } 26 //上传函数,因为子节点值出现修改,就沿着树向上修改父亲的值,一直修改到根节点。 27 long long build(int l,int r,int o){ 28 if(l>r) return 0; 29 if(l==r){ 30 num[o]=1; 31 sum[o]=a[l]; 32 return sum[o]; //如果拆分到不能再分,就把改点作为一个叶子节点,并返回值 33 }else{ 34 num[o]=r-l+1; //区间长度就是这个区间存在的数的个数。 35 sum[o]=build(l,(l+r)/2,o*2)+build((l+r)/2+1,r,o*2+1); 36 return sum[o]; //如果改点不是叶子节点,那么他的和就是左二子的+右儿子的。 37 } 38 } 39 40 void addn(int x,int y,int k,int o,int l,int r){ //加值函数 41 int mid=(l+r)/2; 42 if(x==l&&y==r){ //如果要修改的恰好为一个区间,就直接在区间上操作 43 lazy[o]+=k; //懒标记记录整个区间每个数要加的个数 44 sum[o]+=lazy[o]*(r-l+1); //sum记录整个区间加完数之后的值 45 pushdown(o); //下放标记 46 pushup(o); //上传值 47 return; 48 }else 49 if(x>=mid+1&&y>=mid+1){ //如果要操作的都在区间中点的右边(右孩子),就到右孩子操作 50 addn(x,y,k,o*2+1,mid+1,r); 51 }else 52 if(x<=mid&&y<=mid){ //如果要操作的都在区间中点的左边(左孩子),就到左孩子操作 53 addn(x,y,k,o*2,l,mid); 54 }else 55 if(x<=mid&&y>=mid+1){ //如果该区间横跨左右两个孩子,就将该区间从中间劈开,分别处理 56 addn(x,mid,k,o*2,l,mid); 57 addn(mid+1,y,k,o*2+1,mid+1,r); 58 } 59 } 60 61 long long qiuh(int x,int y,int o,int l,int r){ //求和函数,原谅我不会 求和 的英语TUT 62 int mid=(l+r)/2; 63 if(x==l&&y==r) return (sum[o]+lazy[o]*(r-l+1)); 64 else 65 if(x>=mid+1&&y>=mid+1){ 66 sum[o]+=lazy[o]*(r-l+1); 67 pushdown(o); 68 return (qiuh(x,y,o*2+1,mid+1,r)); 69 } 70 else 71 if(x<=mid&&y<=mid){ 72 sum[o]+=lazy[o]*(r-l+1); 73 pushdown(o); 74 return (qiuh(x,y,o*2,l,mid)); 75 } 76 else 77 if(x<=mid&&y>=mid+1){ 78 sum[o]+=lazy[o]*(r-l+1); 79 pushdown(o); 80 return qiuh(x,mid,o*2,l,mid)+qiuh(mid+1,y,o*2+1,mid+1,r); 81 } 82 } 83 //求和函数的操作方式与加值函数几乎相同,看懂价值函数后很容易理解求和函数…… 84 int main(){ 85 scanf("%ld %ld",&n,&f); 86 for(int i=1;i<=n;i++) scanf("%ld",&a[i]); 87 build(1,n,1); 88 for(int i=1;i<=f;i++){ 89 scanf("%ld",&m); 90 switch (m){ 91 case 1:{ 92 scanf("%ld %ld %ld",&l,&r,&k); 93 addn(l,r,k,1,1,n); 94 break; 95 } 96 case 2:{ 97 scanf("%ld",&k); 98 addn(1,1,k,1,1,n); 99 break; 100 } 101 case 3:{ 102 scanf("%ld",&k); 103 addn(1,1,k*(-1),1,1,n); 104 break; 105 } 106 case 4:{ 107 scanf("%ld %ld",&l,&r); 108 ans=qiuh(l,r,1,1,n); 109 printf("%ld\n",ans); 110 break; 111 } 112 case 5:{ 113 ans=qiuh(1,1,1,1,n); 114 printf("%ld\n",ans); 115 break; 116 } 117 } 118 } 119 return 0; 120 }
转载于:https://www.cnblogs.com/Misaki-Mei/p/7361614.html
线段树(区间修改)模板题 Luogu 2357 守墓人相关推荐
- 【模板】线段树区间修改
区间修改: 区间修改过程类似于区间询问,例如将[ul, ur]内的所有元素都加上v,则进行如下操作: 当当前区间被区间[ul, ur]所包含时, 当前的节点值加上区间长度(r - l + 1)乘以v ...
- python:线段树区间修改 + 区间查询 模板 + 坑点总结
from functools import reduceclass SegTree:'''支持增量更新,覆盖更新,序列更新,任意RMQ操作基于二叉树实现初始化:O(1)增量更新或覆盖更新的单次操作复杂 ...
- 线段树 区间更新模板
一个带区间修改的线段树求和模板: int MAXN = 100005; ll a[100005<<2],ans,p; struct Tree {ll l,r;ll sum,add,mul; ...
- POJ 2777 Count Color (线段树区间修改 + 状态压缩)
题目链接:POJ 2777 Count Color [题目大意] 给你 n 块板子, 编号1--n , 板子的颜色最多30种, 初始时 板子的颜色都是 1: 有两种操作 1 .把给定区间的板子染成一 ...
- HDU - 2871 Memory Control(线段树+区间合并)好题!
题目链接:点击查看 题目大意:给定n个内存和m个操作,分别是: New x:从内存编号1开始向右查找,分配一个长度为x的空间,若找到输出区间的首地址,否则输出Reject New: Free x:释放 ...
- 【UOJ 53】线段树区间修改
[题目描述]: 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 [输入描述]: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...
- HDU 1698 Just a Hook (线段树区间修改+区间查询)
题目链接: 传送门 题意:Pudge对装备钩子进行若干次的强化,强化分为三种分别对应的价值是1,2,3,在经历过若干次操作后,输出钩子对应的总价值,每次强化都是对钩子进行区间修改 解题思路:在明白了题 ...
- hdu1698(线段树/区间修改/求和)
hdu1698"Just a Hook" 题意: 有一个区间s [1,n],每一节si的初始价值为1.定义操作:x y val,将区间[x,y]中的每一个小节的价值改为val.问: ...
- 1631 小鲨鱼在51nod小学(线段树区间修改+单点查询:不用下传lazy的区间修改)
题目描述: 1631 小鲨鱼在51nod小学 鲨鱼巨巨2.0(以下简称小鲨鱼)以优异的成绩考入了51nod小学.并依靠算法方面的特长,在班里担任了许多职务. 每一个职务都有一个起始时间A和结束时间B, ...
最新文章
- golang key map 所有_Golang面试知识点总结
- Linux安装配置Java1.8开发环境
- mysql5.5 配置_MySQL5.5 安装配置方法教程
- 用html和css布局如下图像,[看书][CSS精粹(第2版)]第三章 CSS和图像 HTML网页布局...
- K3CLOUD数据权限授权
- 第二百七十九节,MySQL数据库-pymysql模块操作数据库
- wrapper怎么用_用责任链模式设计拦截器
- [转载] java中数组的反射的探究
- mybatis实现代码自动生成
- linux dump备份svn,svnadmin dump+load库中的某个目录用svndumpfilter 可实现
- 华为手机刷机功能总结
- mysql中一个字符等于几个字节_细说一个汉字等于几个字符,以及汉字,字符,字节,位之间的关系...
- 博客园签名档图片圆角美化
- 算法-贪心/动态规划-买卖股票的最佳时机
- 2021 HTML面试题(最新)不定时更新
- 行测-图形推理-7-相异图形类
- SEO快速建站,八部曲
- 在线运行java测试
- 最近邻搜索|Nearest neighbor search
- 设置CentOS开机启动程序及定时关机