维护区间的线段树

线段树主要就是在在PushUp和Query的时候注意怎么合并左右区间的信息就可以了。对于延迟标记的互相影响完全就是自己跟自己过不去,假如有多种延迟标记的话不妨在访问到一个区间时全部下推(只需要注意叶子层是不能下推的),从其他部分把常数补回来就可以了。

例1 维护加法和(修改:单点加值 询问:区间加法和)

struct SegmentTree {
#define ls (o<<1)
#define rs (o<<1|1)
static const int MAXN = 100000;
ll a[MAXN + 5];
ll st[(MAXN << 2) + 5]; void PushUp(int o) {
st[o] = st[ls] + st[rs];
} void Build(int o, int l, int r) {
if(l == r)
st[o] = a[l];
else {
int m = l + r >> 1;
Build(ls, l, m);
Build(rs, m + 1, r);
PushUp(o);
}
} void Update(int o, int l, int r, int p, ll v) {
if(l == r) {
st[o] += v;
return;
} else {
int m = l + r >> 1;
if(p <= m)
Update(ls, l, m, p, v);
if(p >= m + 1)
Update(rs, m + 1, r, p, v);
PushUp(o);
}
} ll Query(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return st[o];
} else {
int m = l + r >> 1;
ll res = 0;
if(ql <= m)
res = res + Query(ls, l, m, ql, qr);
if(qr >= m + 1)
res = res + Query(rs, m + 1, r, ql, qr);
return res;
}
}
#undef ls
#undef rs
};

例2 维护加法和(修改:区间加值 询问:区间加法和)

struct SegmentTree {
#define ls (o<<1)
#define rs (o<<1|1)
static const int MAXN = 100000;
ll a[MAXN + 5];
ll st[(MAXN << 2) + 5], lazy[(MAXN << 2) + 5]; void PushUp(int o) {
st[o] = st[ls] + st[rs];
} void PushDown(int o, int l, int r) {
if(lazy[o]) {
lazy[ls] += lazy[o];
lazy[rs] += lazy[o];
int m = l + r >> 1;
st[ls] += lazy[o] * (m - l + 1);
st[rs] += lazy[o] * (r - m);
lazy[o] = 0;
}
} void Build(int o, int l, int r) {
if(l == r)
st[o] = a[l];
else {
int m = l + r >> 1;
Build(ls, l, m);
Build(rs, m + 1, r);
PushUp(o);
}
lazy[o] = 0;
} void Update(int o, int l, int r, int ql, int qr, ll v) {
if(ql <= l && r <= qr) {
lazy[o] += v;
st[o] += v * (r - l + 1);
return;
} else {
PushDown(o, l, r);
int m = l + r >> 1;
if(ql <= m)
Update(ls, l, m, ql, qr, v);
if(qr >= m + 1)
Update(rs, m + 1, r, ql, qr, v);
PushUp(o);
}
} ll Query(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return st[o];
} else {
PushDown(o, l, r);
int m = l + r >> 1;
ll res = 0;
if(ql <= m)
res = res + Query(ls, l, m, ql, qr);
if(qr >= m + 1)
res = res + Query(rs, m + 1, r, ql, qr);
return res;
}
}
#undef ls
#undef rs
};

例3 维护最大最小值(修改:区间加值 询问:区间最大最小值)

struct SegmentTree {
#define ls (o<<1)
#define rs (o<<1|1)
static const int MAXN = 1000000;
static const int INF = 0x3f3f3f3f;
int mi[(MAXN << 2) + 5];
int ma[(MAXN << 2) + 5];
int lz[(MAXN << 2) + 5]; void PushUp(int o) {
mi[o] = min(mi[ls], mi[rs]);
ma[o] = max(ma[ls], ma[rs]);
} void PushDown(int o, int l, int r) {
if(lz[o]) {
lz[ls] += lz[o];
lz[rs] += lz[o];
//int m = l + r >> 1;
mi[ls] += lz[o];
mi[rs] += lz[o];
ma[ls] += lz[o];
ma[rs] += lz[o];
lz[o] = 0;
}
} void Build(int o, int l, int r) {
if(l == r) {
mi[o] = 0;
ma[o] = 0;
} else {
int m = l + r >> 1;
Build(ls, l, m);
Build(rs, m + 1, r);
PushUp(o);
}
lz[o] = 0;
} void Update(int o, int l, int r, int ql, int qr, int v) {
if(ql <= l && r <= qr) {
lz[o] += v;
mi[o] += v;
ma[o] += v;
} else {
PushDown(o, l, r);
int m = l + r >> 1;
if(ql <= m)
Update(ls, l, m, ql, qr, v);
if(qr >= m + 1)
Update(rs, m + 1, r, ql, qr, v);
PushUp(o);
}
} int QueryMin(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return mi[o];
} else {
PushDown(o, l, r);
int m = l + r >> 1;
int res = INF;
if(ql <= m)
res = QueryMin(ls, l, m, ql, qr);
if(qr >= m + 1)
res = min(res, QueryMin(rs, m + 1, r, ql, qr));
return res;
}
} int QueryMax(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return ma[o];
} else {
PushDown(o, l, r);
int m = l + r >> 1;
int res = -INF;
if(ql <= m)
res = QueryMax(ls, l, m, ql, qr);
if(qr >= m + 1)
res = max(res, QueryMax(rs, m + 1, r, ql, qr));
return res;
}
}
#undef ls
#undef rs
} st;

这种线段树可以简单拓展:加多一个标记,记录这个区间的最值出自哪个元素。尤其容易维护出最左侧/最右侧的最值。


维护值域的线段树(权值线段树)

需要先离线所有可能的取值,然后离散化到线段树可以接受的空间范围。每个节点存当前的值域的信息,最简单的应用是维护当前值域的元素共有多少个。那么可以在线段树上二分,返回当前线段树中的第k大。这种情形用于在某些情况下替代平衡树的功能,优点是常数相对平衡树而言很小,对于插入数据的顺序有要求,有时可能还要求离线。真正在线维护全树第k大的只有平衡树。

struct SegmentTree {
#define ls (o<<1)
#define rs (o<<1|1)
static const int MAXN = 100000;
int cnt[(MAXN << 2) + 5]; void PushUp(int o) {
cnt[o] = cnt[ls] + cnt[rs];
} void Build(int o, int l, int r) {
if(l == r)
cnt[o] = 0;
else {
int m = l + r >> 1;
Build(ls, l, m);
Build(rs, m + 1, r);
PushUp(o);
}
} //修改值为p的元素的个数,增量为v,且不能为负
void Update(int o, int l, int r, int p, int v) {
if(l == r) {
cnt[o] += v;
if(cnt[o] < 0)
cnt[o] = 0;
return;
} else {
int m = l + r >> 1;
if(p <= m)
Update(ls, l, m, p, v);
if(p >= m + 1)
Update(rs, m + 1, r, p, v);
PushUp(o);
}
} //查询<=x的元素的个数
int GetRank(int o, int l, int r, int x) {
if(r <= x) {
return cnt[o];
} else {
int m = l + r >> 1;
if(x <= m)
return GetRank(ls, l, m, x);
else
return cnt[ls] + GetRank(rs, m + 1, r, x);
}
} //查询最小的x,使得<=x的元素个数>=rk(第rk小)
int GetValue(int o, int l, int r, int rk) {
if(l == r) {
return l;
} else {
int m = l + r >> 1;
if(cnt[ls] >= rk)
return GetValue(ls, l, m, rk);
else
return GetValue(rs, m + 1, r, rk - cnt[ls]);
}
}
#undef ls
#undef rs
} st;

注意:这里面传入的参数都应该是离散化之后的值。权值线段树不能用一次递归实现GetPrev()和GetNext(),需要通过给离散化的值做出一些修改,然后组合GetRank()和GetNext()才可以实现。优势在于代码短,常数小。

动态开点权值线段树

不再需要提前离散化了。

模板 - 数据结构 - 线段树/SegmentTree的更多相关文章

  1. 洛谷——P3373 【模板】线段树 2&& B 数据结构

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 线段树维护区间乘法 1.如何 ...

  2. 「模板」 线段树——区间乘 && 区间加 && 区间求和

    「模板」 线段树--区间乘 && 区间加 && 区间求和 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long l ...

  3. 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解

    什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...

  4. 洛谷P3372 【模板】线段树 1

    P3372 [模板]线段树 1 153通过 525提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 [模板]线段树1(AAAAAAAAA- [模板]线段树1 洛谷 ...

  5. 洛谷P3373 【模板】线段树 2

     P3373 [模板]线段树 2 47通过 186提交 题目提供者HansBug 标签 难度提高+/省选- 提交  讨论  题解 最新讨论 为啥WA(TAT) 题目描述 如题,已知一个数列,你需要进行 ...

  6. hdu 3074 Multiply game(模板级线段树)

    离机房关门还有十分钟,这点时间能干些什么?故作沉思地仰望星空,重新捋一下一天的学习进度,或者,砍掉一棵模板级线段树. 纯模板,就是把单点更新,区间求和改为单点更新,区间求积. 1A. #include ...

  7. 线段树练习 3&&P3372 【模板】线段树 1

    题目描述 Description 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述 Input Description 第一行一个正整数n,接下 ...

  8. Luogu3373【模板】线段树2

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

  9. 洛谷 P3373 【模板】线段树 2 解题报告

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

随机推荐

  1. PowerDesigner 连接数据库,更新数据库;

    首先:以管理员身份运行Powerdesigner 一.连接服务器的某个数据库: 点击新建数据源图标 选择数据源类型:用户数据源:这里说是只用于当前机器,实际局域网里的都可以. Successfully ...

  2. pssh安装及使用

    pssh全称是parallel-ssh,基于Python编写的并发在多台服务器上批量执行命令的工具,它支持文件并行复制.远程并行执行命令.杀掉远程主机上的进程等:该工具可以视作ansible的简化版本 ...

  3. O - Can you find it?(二分查找)

    O - Can you find it? Time Limit:3000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64 ...

  4. Python学习日记(二) list操作

    l = ['a','b','c','d',1,2,[3,'e',4]] 1.list.append() 在list的结尾新增一个新的元素,没有返回值,但会修改原列表 l.append(5) print ...

  5. Flume 初探

    Apache 是一个高可用.高可靠的,分布式的海量日志采集.聚合.传输系统,基于流式架构,灵活简单. Flume 最主要的作用就是实时读取服务器本地磁盘的数据,将数据写入HDFS中. Flume组成架 ...

  6. mysql8.x 新版本jdbc连接方式

    旧版本,MySQL Connector/J 5.x 版本的连接方式:url = jdbc:mysql://localhost:3306/thrcloud_db01?useUnicode=true&am ...

  7. 数据库系统load飙高问题解决思路

    工作过程中有时候会接收到数据库服务器器load 飙高的报警,比如:  load1 15.25 base: 8.52,collect time:2014-08-30 如何处理load 异常飙高的报警呢? ...

  8. flask基础2

    一个装饰器无法装饰多个函数的解决方法 当我们想在flask的多个视图函数前添加同一个装饰器时,如果什么都不做会报一个错 AssertionError: View function mapping is ...

  9. 题解 洛谷P3745 【[六省联考2017]期末考试】

    这题有点绕,我写了\(2h\)终于搞明白了. 主要思路:枚举最晚公布成绩的时间\(maxt\),然后将所有公布时间大于\(maxt\)的课程都严格降为\(maxt\)即可. 在此之前,还要搞清楚一个概 ...

  10. 《The One!团队》第八次团队作业:Alpha冲刺

    项目 内容 作业所属课程 所属课程 作业要求 作业要求 团队名称 < The One !> 作业学习目标 (1)掌握软件测试基础技术.(2)学习迭代式增量软件开发过程(Scrum) 团队项 ...