线段树是一棵二叉搜索树,常用于解决区间问题。
线段树每一个非叶子节点[left,right],它的左二子区间[left,(left+right)/2],右儿子区间[(left+right)/2+1,right]。
线段树的结构体,内元素包括,此节点包含区间[left,right],和value
//线段树结构体
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1<<20;
const int N;
struct Node {
int value; //检点区间权值
int left, right; //区间(left,right)
}node[maxn];
int father[N]; //每个点(当区间长度为0时,对应的结构体数组下标)
线段树的建立
void BuildTree(int i, int left, int right)
{
node[i].left = left; //写入第i个结点的左区间
node[i].right = right; //写入第i个结点的右区间
node[i].value = 0; //区间权值初始化为0
if(left == right) {
father[left] = i; //知道每个点对应的序号(结点的下标)
return;
}
//建立左子树
BuildTree(i<<1, left, (left+right)/2);
//建立右子树
BuildTree((i<<1)+1, (left+right)/2+1, right);
}
单点更新线段树
因为事先用father[]数组保存过每个节点对应的的下标,因此只要知道第几个点,就知道这个点在树中的位置(即下标),这样就只要一路向上更新上去即可。
void UpdateTree(int r1)
{
if(r1 == 1) return; //已找到祖先
int fi = r1/2; //ri的父节点
int a = node[fi<<1].value; //该父节点的两个孩子
int b = node[(fi<<1)+1].value; //右
node[fi].value = (a > b) ? a : b;
UpdateTree(ri/2); //递归更新,有父节点往上找
}
查询区间最大值
讲一段区间从上往下拆开,直到存在有完全重合的区间停止。
int Max = -1<<20;
void Query(int i, int l, int r)
{
if(node[i].left == l && node[i].right == r) {
Max = (Max < node[i].value) ? (node[i].value) : (Max);
return;
}
i = i << 1;
if(l <= node[i].right) { //左区间有涉及
if(r <= node[i].right) //全包含于左区间,则查询形态不变
Query(i, l, r);
else //半包含于左区间,则查询区间拆分,左端点不变,右端点变为左区间的右孩子端点
Query(i, l, node[i].right);
}
i += 1;
if(r >= node[i].left) { //右区间有涉及
if(l >= node[i].left) //全包含于右区间,则查询形态不变
Query(i, l, r);
else //半包含于右区间,则查询区间拆分
Query(i, node[i].left, r);
}
}