数据结构(终极线段树篇)

摘要:

问题的提出:如何解决多样化的区间操作问题?

solve:线段树!!!

关键字:

线段树,可持久化线段树,权值线段树,线段树森林,动态开点线段树,区间操作,线段树应用。

前言:

区间操作问题的解决方法极多,如:树状数组,RMQ等。

所有这些数据结构都具有一定的局限性,都具有各自的优势和劣势。

而线段树无疑是这些数据结构中性价比最高的数据结构!(Ps:搞什么线段树,暴力出奇迹啊!)

线段树,线段树,线段树。

顾名思义:树上每一个节点表示一个线段(区间),每一次对一整个线段(区间)进行操作,棒棒。

第1节讲述了线段树的基本性质。

第2节实现了基本线段树。

第3节讲述动态开点线段树。

第4节讲述权值线段树。

//第5节讲述线段树森林。

//第6节讲述线段树的重要应用和例题。

1.线段树的基本性质:

1.0基本线段树的性质:

1.每个节点u表示一个区间[l,r]。若节点u非叶子节点,则u有两个儿子。设(mid=(l+r)/2)。                                                 左儿子为[l,mid],节点编号为u*2。                                                                                                                                   右儿子为[mid+1,r],节点编号为u*2+1。

2.线段树的深度为O(lgn)

3.线段树的节点个数为O(n),常数约为4。

4.线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。

思考:为何左儿子不为[l,mid+1],为何左儿子的节点编号为u*2。

思考:如何证明线段树的节点个数为O(n),且常数为4。

思考:如何证明线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。

倘若我们只需要单点修改,我们只需要找到相应的叶子节点,更改叶子节点并沿路更新。

但倘若我们需要区间修改,我们却需要引入lazy tag(懒惰标记)。

每一次操作都先下放懒标记,清空标记,再进行相应的操作。

如上图:若将[1,5]全加上3。

需要标记tag[1,5]=3,下一次操作到[1,5]时下放。

tag[1,3]=3,tag[4,5]=3,tag[1,5]=0,sum[1,5]+=3*5;

2.基本线段树的实现:

2.1基本线段树的区间加,询问区间和:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1000005;
ll a[MAXN];
ll read()
{char c=getchar(); ll f=1,x=0;while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}return f*x;
}
struct Segment_tree
{struct node{int l,r;ll sum,tag;} tree[MAXN<<2];void up(int x){ tree[x].sum=tree[x<<1].sum+tree[(x<<1)|1].sum; }void down(int x){int num1=(tree[x<<1].r-tree[x<<1].l+1);int num2=(tree[(x<<1)|1].r-tree[(x<<1)|1].l+1);tree[(x<<1)|1].sum+=tree[x].tag*num2;  tree[x<<1].tag+=tree[x].tag;      tree[(x<<1)|1].tag+=tree[x].tag;       tree[x<<1].sum+=tree[x].tag*num1; tree[x].tag=0;}void build(int x,int l,int r,ll *a){tree[x].l=l;tree[x].r=r;if (l==r){tree[x].sum=a[l];tree[x].tag=0;return;}int mid=(l+r)>>1;build(x<<1,l,mid,a);build((x<<1)|1,mid+1,r,a);up(x);}void change(int x,int l,int r,ll y){if (tree[x].l>=l&&tree[x].r<=r){tree[x].tag+=y;tree[x].sum+=y*(tree[x].r-tree[x].l+1);return;}down(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) change(x<<1,l,r,y);else if (l>mid) change((x<<1)|1,l,r,y);else {change(x<<1,l,mid,y);change((x<<1)|1,mid+1,r,y);}up(x);}ll query(int x,int l,int r){if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;down(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) return query(x<<1,l,r);else if (l>mid) return query((x<<1)|1,l,r);else return query(x<<1,l,mid)+query((x<<1)|1,mid+1,r);}void print(int x){for (int i=1;i<=x;i++) printf("%d %d %d\n",tree[i].l,tree[i].r,tree[i].sum);printf("\n");}
} segment_tree;
int main()
{int n=read(),m=read();for (int i=1;i<=n;i++) a[i]=read();segment_tree.build(1,1,n,a);for (int i=1;i<=m;i++){int c=read();if (c==1){int x=read(),y=read(),z=read();segment_tree.change(1,x,y,z);}else{int x=read(),y=read();printf("%lld\n",segment_tree.query(1,x,y));}}return 0;
}

3.动态开点线段树

动态开点线段树,实现单点加,区间求和。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1000005; const int MAXS=1e9;
ll read()
{char c=getchar(); ll f=1,x=0;while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}return f*x;
}
struct Dynamic_segment_tree
{int nodenum=0;struct node{int l,r,leftnode,rightnode;ll sum;} tree[MAXN<<2];void change(int &x,int l,int r,int t,ll y){if (!x){nodenum++;x=nodenum;}tree[x].l=l;tree[x].r=r;tree[x].sum+=y;if (l==r) return;int mid=(tree[x].l+tree[x].r)>>1;if (t<=mid) change(tree[x].leftnode,l,mid,t,y);else change(tree[x].rightnode,mid+1,r,t,y);}ll query(int x,int l,int r){if (!x) return 0;if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) return query(tree[x].leftnode,l,r);else if (l>mid) return query(tree[x].rightnode,l,r);else return query(tree[x].leftnode,l,mid)+query(tree[x].rightnode,mid+1,r);}void print(int x){for (int i=1;i<=x;i++) printf("%d %d %d %d %d\n",tree[i].l,tree[i].r,tree[i].leftnode,tree[i].rightnode,tree[i].sum);printf("\n");}
} dynamic_segment_tree;
int main()
{int n=read(),m=read(),root=0;for (int i=1;i<=m;i++){int c=read(),x=read(),y=read();if (c==1) dynamic_segment_tree.change(root,1,MAXS,x,y);else printf("%d\n",dynamic_segment_tree.query(root,x,y));//dynamic_segment_tree.print(20);}return 0;
}

4.权值线段树

权值线段树,就是每一个线段树的叶子节点记录一种值的信息,常用于记录区间内某权值的出现个数。

这和一般的线段树差不多,只是节点的意义不同。

例题:求逆序对个数

未完待续

数据结构(终极线段树篇)相关推荐

  1. 307. Range Sum Query - Mutable | 307. 区域和检索 - 数组可修改(数据结构:线段树,图文详解)

    题目 https://leetcode.com/problems/range-sum-query-mutable/ 吐槽官方题解 这题的 英文版官方题解,配图和代码不一致,而且描述不清:力扣国内版题解 ...

  2. 数据结构之线段树入门(单点更新区间查询)

    线段树是学习数据结构必须学习的一种数据结构,在ACM,蓝桥等比赛中是经常出现的.利用线段树解题,会使得题目简单易理解.而且线段树是数据结构中比较基础而且用的很多的一种. 线段树定义 线段树是一种二叉搜 ...

  3. c++ 数据结构之 线段树

    线段树是一种数据结构,是一种二叉树.线段树将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.线段树对于区间求和等区间操作能够实现复杂度为O(logn)的操作,故得以广泛利用.修改一个 ...

  4. 【BZOJ4370】【IOI2015】horses 数据结构 平衡树+线段树

    4370: [IOI2015]horses马 Time Limit: 30 Sec Memory Limit: 1500 MB Description 像他的祖先一样,Mansur喜欢繁殖马匹.目前, ...

  5. 【数据结构】线段树的扩展与应用

    线段树是一种非常基础的数据结构,但有的时候仅仅是普通的线段树无法满足需求,那么我们就要对其进行一些扩展. Chapter1:标记永久化 实现 普通的线段树通过懒标记(Lazy Tag)以 O ( n ...

  6. 蒟蒻的ACM数据结构(一)-线段树

    浅谈线段树的指针写法 一.基本概念 二.代码实现与基本操作 0.基础数据结构 1.建树 built函数 2. 单点查询 3.单点修改 4.区间查询 5.区间修改 三.优化 (一). Lazy-Tag懒 ...

  7. 0x43.数据结构进阶 - 线段树

    目录 一.基础线段树 线段树的建树 线段树的单点修改 线段树的区间查询 线段树的延迟标记(懒惰标记) 1.POJ3486 ASimpleProblemwithIntegersA\ Simple\ Pr ...

  8. 数据结构:线段树及ST算法比较

    ST算法是一种高效的计算区间最值的方法. 他的思想是将询问区间分解成两个最长的二次幂的长度的区间并集的形式. 所以与线段树不同,这种区间分解其实存在相交的分解. 因此ST算法能维护的只是一些简单的信息 ...

  9. 数据结构之线段树进阶(区间更新lazy标记)

    之前说了线段树的点更新和区间求和.其实点更新是区间更新的一种最基础的做法.我们把一个点想像成一个区间的话,不就是最简单的区间更新了嘛. 为什么要把区间更新和点更新分开来看呢?假如我们对区间[l,r]进 ...

最新文章

  1. 谢文睿:西瓜书 + 南瓜书 吃瓜系列 2. 多元线性回归
  2. 史上最全的Excel导入导出(easyexcel版)
  3. 吴恩达新书《Machine Learning Yearning》完整中文版 PDF 下载!
  4. arm linux qt 输入法,基于ARM9和Qt的中文手写输入法的设计与实现
  5. [HNOIAHOI2018] 转盘(线段树维护单调栈)
  6. 什么情况使用 weak 关键字,相比 assign 有什么不同?
  7. 发那科攻丝回退参数_乐享:发那科MF选配功能诊断小结
  8. 42表盘直径是从哪测量_爱彼15703和15710区别在哪?背透和密底哪个更好?
  9. 如何查看谷歌卫星地图每级分辨率的两种方法
  10. 关于全国大学生集成电路创新创业大赛
  11. 高频消息中间件面试题解析
  12. leetcode【中等】781、森林中的兔子
  13. UITableView在iOS15中显示混乱的问题
  14. 免拆机,Kindle固件版本5.10.3~5.13.3如何越狱?简单、易操作版
  15. webService--java
  16. 2018达内Web前端开发教程(最全)
  17. 2018香港银行卡开户、购汇、跨境汇款【全攻略】
  18. JQuery下拉框与复选框
  19. 高中数学40分怎么办_高中数学不好怎么办
  20. [小说]魔王冢(48)诏告

热门文章

  1. 宝贝,我帮你清了购物车哦!
  2. 兄弟,就你这智商就别出轨了吧?
  3. 奥林匹克数学竞赛教练员汇编,最牛奥数资料全集!
  4. mysql映射超_Hibernate的映射类型 hibernate mysql映射类型
  5. 100例经典炒菜_Python3经典100例(②)
  6. c语言环境窗口组成,如何搭建C语言环境
  7. oracle外网监听端口,oracle 11g 修改默认监听端口1521
  8. uibot在子程序执行js失败_使用 Node.js 将珍藏的 bash 脚本封装成命令行工具
  9. python判断字符串里的字符_python 判断检测字符串中是否包含指定字符或字符串(比如:?)...
  10. 山西农业大学计算机科学与技术分数线,2016年山西农业大学计算机科学与技术专业在湖北录取分数线...