参考两位神犇的博客:
https://www.cnblogs.com/TenosDoIt/p/3453089.html
https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html
好吧终于学了线段树,核心思想是二分,然后构建一个完全二叉树,根节点表示区间[l,r],左儿子表示区间[l,mid],右儿子表示区间[mid+1,r],叶节点l==r。
每个节点可以存区间和,区间最值。
预处理时间O(n),查询时间O(logn),更新时间O(logn),需要额外的空间O(n)。
讲道理还是蛮好理解的,懒标记做区间修改待更新。。。
//线段树模板
//此模板为维护区间和的线段树为例
struct node
{
int l,r,w;//l,r分别表示区间左右端点,w表示区间和
}tree[4*n+1];//结构体要开4倍空间
//建树
//l:左端点;r:右端点;root:当前根节点的下标
void build(int l,int r,int root)
{
tree[root].l=l;tree[root].r=r;
if(l==r)
tree[root].w=arr[l];
else
{
int mid=(l+r)/2;
build(l,mid,root*2);//递归构造左子树
build(mid+1,r,root*2+1);//递归构造右子树
tree[root].w=tree[k*2].w+tree[k*2+1].w;//根据左右子树根节点的值,更新当前根节点的值
}
}
//单点查询
//单点查询的思路就是二分
//区间查询
//区间查询的思想是选出一些区间,使他们相连后恰好涵盖整个查询区间,因此线段树适合解决“相邻的区间的信息可以被合并成两个区间的并区间的信息”的问题。
//以查询[x,y]的区间和为例
int query(int k)
{
if(tree[k].l>=x&&tree[k].r<=y)
{
ans+=tree[k].w;//当前区间被完全包含在[x,y]中,直接加上
return;
}
int m=(tree[k].l+tree[k].r)/2;
if(x<=m) sum(k*2);//当前区间的左半边与[x,y]有重合,则递归查询左子树
if(y>m) sum(k*2+1);//当前区间的右半边与[x,y]有重合,则递归查询右子树
}
//单点修改
//单点修改的思路是找到二分x的位置,根据建树原理,修改每个节点
//以给第k个数加y为例
void add(int k)
{
if(tree[k].l==tree[k].r)
{
tree[k].w+=y;
return ;
}
int m=(tree[k].l+tree[k].r)/2;
if(x<=m) add(k*2);
else add(k*2+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;//将所有包含k节点的区间更新
}
//区间修改
//
/**/