初步线段树
——HM
当你遇到了这样的题目时:
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:L不超过当前数列的长度。(L>=0)
2、 插入操作。
语法:A n
功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。
限制:n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
你的第一反应是什么? 当然是暴力模拟了。
然后我们来分析一下数据:操作个数<=20,0000,暴力做的话,时间复杂度O(m^2),绝对TLE,可能连40都达不到。
于是线段树横空出世了。
线段树是一种二叉查找树,充分利用了二叉树“快”的特点,可以高效率完成区间修改、区间查询、区间最值等操作。
大体思路是:将一个区间划分为一些单元区间,每个单元区间对应线段树中的一个叶节点。其有以下几种操作:
① 区间修改(change(x,tree)):将ai~aj全部加x。
② 删除元素(Delete(x,tree)):从线段树tree中删除元素x。
③ 查找元素(Search(x,tree)):返回树中元素x的地址指针。
速度都为O(logn)
当然还有一些更加实用的操作,但稍微有些复杂,会举例详细讲解。
线段树的构造:线段树大多都是用结构体递归来构造的,代码如下:
#include <bits/stdc++.h>
using namespace std;
struct Node{
int Left, Right; Node *LeftChild, *RightChild;//树节点机构体,体现递归
};
void build(Node *cur, int L, int r);
int main()
{
Node *root;
root=new Node;//一定要new
int L,r;
cin>>L>>r;
build(root,L,r);
return 0;
}
void build(Node *cur, int L, int r)
{
cur->Left = L;cur->Right = r;
if (L!=r){
cur->LeftChild=new Node;
cur->RightChild = new Node;
build(cur->LeftChild,L,(L+r)/2);//递归实现建树
build(cur->RightChild,(L+r)/2+1, r);//同上
}
else{
cur->LeftChild=NULL;
cur->RightChild=NULL;//停止递归
}
}
这样,一个线段树便建成了。
参考一些《算法竞赛》的内容。