关于线段树:

本随笔参考例题

     P3372 【模板】线段树 1

  所谓线段树就是把一串数组拆分成一个一个线段形成的一棵树。

  比如说像这样的一个数组1,2,3,4,5:

1 ~ 5

/           \

1 ~ 3           4 ~ 5

/   \         /   \

1 ~ 2      3    4        5

/     \

1           2

   如图所示,这就类似于线段树。

线段树之所以称之为树是因为他分解出来的样子类似于树,如上图所示。

  1. 初始化

    关于初始化个人认为没什么好讲,由于线段树需要建树所以个人比较懒就用了#define

   1 #define Mid (l+r)/2
  2 #define Left root*2 //众所周知,一个节点的左儿子下标就是当前节点的下标(root)*2
  3 #define Right root*2+1 //一个节点的右儿子下标就是当前节点的下标(root)*2+1
  4 const long long maxn=1000000;
  5 struct tr{ //用结构体代替一个节点
  6 long long left_son,right_son;//节点表示的区间left_son~right_son
  7 long long sum,lazy; //该节点区间的和,以及臭名昭著的懒标记
  8 }tree[maxn*2+1];
  9 long long a[maxn*2+1]; //那个数组
  10 long long n,m; //n表示a数组的数字个数,m表示操作次数

    2. 建树

  关于建树:

   1 void Build(long long root,long long l,long long r){//root表示当前遍历到的节点的下标,l和r表示表示l~r的区间
  2 tree[root].left_son=l; //赋值当前节点表示的区间
   3 tree[root].right_son=r;
   4 if(l==r){ //如果左指针与右指针重合说明遍历到了叶子节点,也就是最下方的节点
   5 tree[root].sum=a[l]; //于是就给他赋上a数组的值其实a[l]也可以写a[r],无所谓
  6 return; //接着返回构造下一个节点
   7 }
   8 Build(Left,l,Mid); //遍历他的左儿子(下标为Left)与右儿子(下标为Right),并且缩小区间用类似二分的方法
   9 Build(Right,Mid+1,r);
  10 tree[root].sum=tree[Left].sum+tree[Right].sum; //当左右儿子都有值的时候就构造自己,然后返回构造自己的父亲
  11 }

   具体就是将一个大的区间(比如上面例子1~5)一直分解成子区间,直到叶子节点为止然后再往上构造整棵树。

  3. 懒标记,区间修改,区间查询

什么是懒标记呢?这一点要和区间修改和区间查询一起讲。

顾名思义为了偷懒而做的标记(逃~

      为了节省时间我们可以在修改线段树某个数字的时候现标记出那个要修改的数字,在区间查询的时候再根据标记来修改。

   那么先讲那个呢?先讲

   1.懒标记

  1 void Lazy(long long root){                                                                //root表示当前节点
  2 if(tree[root].lazy){ //如果当前节点有懒标记
  3 tree[Left].sum+=tree[root].lazy*(tree[Left].right_son-tree[Left].left_son+1); //那么就会修改当前节点的值首先要改左儿子与右儿子的值
  4 tree[Right].sum+=tree[root].lazy*(tree[Right].right_son-tree[Right].left_son+1); //既然是区间修改,那么本题要求区间加某个数,所以当前儿子要加上他所表示的区间的数的个数个要加上的那个数
  5 tree[Left].lazy+=tree[root].lazy; //改了左右儿子的值就要改左右儿子的左右儿子的值,所以也要往下修改所以就要使用当前节点儿子的懒标记来修改孙子
  6 tree[Right].lazy+=tree[root].lazy;
  7 tree[root].lazy=0; //当前节点修改成功(还要改儿子)
  8 }
  9 }

   接着讲

   2.区间修改

   1 void Update(long long root,long long l,long long r,long long num){         //root的表示当前节点,l和r表示要修改的区间,num表示要加的数
   2 if(r<tree[root].left_son||tree[root].right_son<l)return;         //如果当前找到的区间不是理想区间那么就返回
   3 if(l<=tree[root].left_son&&r>=tree[root].right_son){           //如果是当前的区间
   4 tree[root].sum+=num*(tree[root].right_son-tree[root].left_son+1); //那么就修改当前的值,与之前懒标记中的方法类似
   5 tree[root].lazy+=num;                          //由于还要修改儿子,所以标记懒标记,然后退出继续寻找合适区间
   6 return;
   7 }
   8 Lazy(root); //修改儿子节点
   9 Update(Left,l,r,num);               //遍历左右儿子
  10 Update(Right,l,r,num);
  11 tree[root].sum=tree[Left].sum+tree[Right].sum; //当前节点的值赋值成左右儿子相加的和
  12 }

   那么随后讲讲

    3.区间查询(本题要查询一段区间的和):

  1 long long Search(long long root,long long l,long long r){            //root代表当前节点,l和r表示要查找的区间
  2 if(r<tree[root].left_son||tree[root].right_son<l)return 0;         //如果不是想找的区间就返回没有值0
  3 if(l<=tree[root].left_son&&r>=tree[root].right_son)return tree[root].sum;//如果是满意的区间那么就返回这个区间的值
  4 Lazy(root);          //将未修改的有懒标记的值加以修改
  5 long long ans=0;       //开一个数记录和
  6 ans+=Search(Left,l,r); //加上左儿子的值
  7 ans+=Search(Right,l,r);   //加上右儿子的值
  8 return ans;          //返回答案
  9 }//大功告成~~QWQ

    那么完整代码就是:

    

 1 #include<bits/stdc++.h>
2 using namespace std;
3 #define Mid (l+r)/2
4 #define Left root*2
5 #define Right root*2+1
6 const long long maxn=1000000;
7 struct tr{
8 long long left_son,right_son;
9 long long sum,lazy;
10 }tree[maxn*2+1];
11 long long a[maxn*2+1];
12 long long n,m;
13 void Build(long long root,long long l,long long r){
14 tree[root].left_son=l;
15 tree[root].right_son=r;
16 if(l==r){
17 tree[root].sum=a[l];
18 return;
19 }
20 Build(Left,l,Mid);
21 Build(Right,Mid+1,r);
22 tree[root].sum=tree[Left].sum+tree[Right].sum;
23 }
24 void Lazy(long long root){
25 if(tree[root].lazy){
26 tree[Left].sum+=tree[root].lazy*(tree[Left].right_son-tree[Left].left_son+1);
27 tree[Right].sum+=tree[root].lazy*(tree[Right].right_son-tree[Right].left_son+1);
28 tree[Left].lazy+=tree[root].lazy;
29 tree[Right].lazy+=tree[root].lazy;
30 tree[root].lazy=0;
31 }
32 }
33 void Update(long long root,long long l,long long r,long long num){
34 if(r<tree[root].left_son||tree[root].right_son<l)return;
35 if(l<=tree[root].left_son&&r>=tree[root].right_son){
36 tree[root].sum+=num*(tree[root].right_son-tree[root].left_son+1);
37 tree[root].lazy+=num;
38 return;
39 }
40 Lazy(root);
41 Update(Left,l,r,num);
42 Update(Right,l,r,num);
43 tree[root].sum=tree[Left].sum+tree[Right].sum;
44 }
45 long long Search(long long root,long long l,long long r){
46 if(r<tree[root].left_son||tree[root].right_son<l)return 0;
47 if(l<=tree[root].left_son&&r>=tree[root].right_son)return tree[root].sum;
48 Lazy(root);
49 long long ans=0;
50 ans+=Search(Left,l,r);
51 ans+=Search(Right,l,r);
52 return ans;
53 }
54 int main(){
55 cin>>n>>m;
56 for(long long i=1;i<=n;i++)cin>>a[i];
57 Build(1,1,n);
58 for(long long i=1;i<=m;i++){
59 long long aa=0,bb=0,cc=0,dd=0;
60 cin>>aa;
61 if(aa==1){
62 cin>>bb>>cc>>dd;
63 Update(1,bb,cc,dd);
64 }
65 else {
66
67 cin>>bb>>cc;
68 cout<<Search(1,bb,cc)<<endl;
69 }
70 }
71 return 0;
72 }

  线段树结束,如果有多种方式修改不妨再加一个懒标记QWQ。(scanf()没有cin好看)(逃~

  希望可以帮助刚学或想学线段树的童鞋们。

线段树入门详解,洛谷P3372 【模板】线段树 1的更多相关文章

  1. 绝对是全网最好的Splay 入门详解——洛谷P3369&BZOJ3224: Tyvj 1728 普通平衡树 包教包会

    平衡树是什么东西想必我就不用说太多了吧. 百度百科: 一个月之前的某天晚上,yuli巨佬为我们初步讲解了Splay,当时接触到了平衡树里的旋转等各种骚操作,感觉非常厉害.而第二天我调Splay的模板竟 ...

  2. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  3. 线段树板子1(洛谷P3372)

    传送 一道线段树板子(最简单的) 似乎之前在培训里写过线段树的样子?不记得了 何为线段树? 一般就是长成这样的树,树上的每个节点代表一个区间.线段树一般用于区间修改,区间查询的问题. 我们如何种写一棵 ...

  4. 差分约束详解&&洛谷SCOI2011糖果题解

    差分约束系统: 如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference const ...

  5. 树链剖分【洛谷P2590】 [ZJOI2008]树的统计

    P2590 [ZJOI2008]树的统计 题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把 ...

  6. 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)

    洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...

  7. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  8. Linq之旅:Linq入门详解(Linq to Objects)【转】

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html Linq之旅:Linq入门详解(Linq to Objects) 示例代码下载:Linq之 ...

  9. Linq之旅:Linq入门详解(Linq to Objects)(转)

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html 示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细 ...

随机推荐

  1. ABBYY FineReader 15 安装教程

    ABBYY FineReader 是一款出名的OCR文字识别工具,它包含文档转换.数据捕获等功能,文字识别率较高.能够带来快速.简单.易用的文字识别体验,从而提高工作效率.下面就为大家讲解ABBYY ...

  2. 二 HTML文档基本结构

    2.1 HTML5文档结构: HTML5文档结构包括头部(head).主体(body)两大部分. 2.1.1<!DOCTYPE>声明 引用官方的DTD文件,在HTML5之前版本,如xHTM ...

  3. 面试官问Linux下如何编译C程序,如何回答?为你编译演示

    文章来源:嵌入式大杂烩 作者:ZhengNL Windows下常用IDE来编译,Linux下直接使用gcc来编译,编译过程是Linux嵌入式编程的基础,也是嵌入式高频基础面试问题. 一.命令行编译及各 ...

  4. Java多线程中的wait/notify通信模式

    前言 最近在看一些JUC下的源码,更加意识到想要学好Java多线程,基础是关键,比如想要学好ReentranLock源码,就得掌握好AQS源码,而AQS源码中又有很多Java多线程经典的一些应用:再比 ...

  5. 这些鲜为人知的前端冷知识,你都GET了吗?

    背景 最近公司项目不多,比较清闲,划水摸鱼混迹于各大技术博客平台,瞬间又GET了好多前端技能,一些属于技巧,一些则是闻所未闻的冷知识,一时间还消化不过来,不由的发出一声感叹! 前端可真是博大精深 于是 ...

  6. 16.java设计模式之迭代器模式

    基本需求: 展示一个学校的结构,比如一个学校下面有多个学院,学院下面有多个系,对其节点主要是遍历,与组合模式略有不同 传统方案: 学校<-学院<-系 依次继承 这种方式,在一个页面中展示出 ...

  7. 05_Content Provider

    Content Provider是内容提供器,与内容(数据)的存取(存储.获取)有关,是Android应用程序的四大组成部分之一,是Android中的跨应用访问数据机制. 数据库在Android当中是 ...

  8. rest-framework 版本控制

    一 作用: 用于版本的控制 二 内置的版本控制类: from rest_framework.versioning import QueryParameterVersioning,AcceptHeade ...

  9. moviepy AudioClip帧处理ValueError: The truth value of array with more than one element is ambiguous

    ☞ ░ 前往老猿Python博文目录 ░ 一.环境 操作系统:win7 64位 moviepy:1.0.3 numpy:1.19.0 Python:3.7.2 二.应用代码及报错信息 程序代码 if ...

  10. 第14.10节 Python中使用BeautifulSoup解析http报文:html标签相关属性的访问

    一. 引言 在<第14.8节 Python中使用BeautifulSoup加载HTML报文>中介绍使用BeautifulSoup的安装.导入和创建对象的过程,本节介绍导入后利用Beauti ...