蒟蒻曾经就学过线段树,但是最近才弄懂(惭愧
线段树,初次听闻这样一个高大上的名字,以为是解决什么惨绝人寰的问题
没想到,竟然只是处理区间的。。。
当时我小小的脑袋充满大大的疑惑
处理区间emmm普通的数组不就能解决了吗
虽然普通数组单点维护查询值复杂度O(1)
但是区间维护查询复杂度为O(n)
对于某些数据量大的题是会t的
这时候聪明的大脑袋会提醒你,还有个前缀和数组呢
虽然前缀和数组计算区间和只需O(1)
但是维护的时候却要O(n)
这样看来,有没有比较均衡的方法呢
那就是线段树
无论是维护还是查询,统统都是O(logn)
优秀~
首首先先,我们需要知道线段树是一颗二叉树
所以一个节点编号node的俩子节点必然是node2(node<<1)和node2+1(node<<1|1)
首先,一个节点需要有以下属性
struct node
{
int l;///区间左端点
int r;///区间右端点
int sum;///区间和
}tr[maxn<<2];
至于为什么数组要开4倍(能用就行)
接下来就是如何建立这个线段树
void build(int node ,int l ,int r )
{
tr[node].l=l;
tr[node].r=r;
int mid=l+r>>1;
if(l==r)
{
tr[node].sum=a[l];
return ;
}
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
tr[node].sum=tr[node<<1].sum+tr[node<<1|1].sum;
}
以及区间查询
int query(int l,int r,int node)
{
if(tr[node].l==l&&tr[node].r==r)
return tr[node].sum;
/*if(tr[node].add)
push_down(node);*////区间更新下推lazytag
int mid=tr[node].l+tr[node].r>>1;
if(r<=mid) return query(l,r,node<<1);
else if(l>mid) return query(l,r,node<<1|1);
else return query(l,mid,node<<1)+query(mid+1,r,node<<1|1);
}
单点维护
void update(int x/*查找x号元素*/ ,int y ,int node )///单点更新,将x号元素值更新为y
{
if(tr[node].l==x&&tr[node].r==x)
{
tr[node].sum=y;
return ;
}
int mid=tr[node].l+tr[node].y>>1;
if(x<=mid) update(x,y,node<<1);
else update(x,y,node<<1|1);
tree[node].sum=tr[node<<1].sum+tr[node<<1|1].sum;
}
区间维护
不同于单点维护,因为区间维护需要操作的是区间内所有节点,每次都更新的话效率较低,所以需要建立一个lazytag,用来先储存区间的操作,等到查询的时候再并到线段树里
即:如果某个区间内所有点都被增加了相同的值,则在lazytag中做上标记(以及add的值)
struct node
{
int l;
int r;
int sum;
int add;///lazy操作所有增加的值
}tr[maxn<<2];
在后续访问到有lazytag的节点时,不用下推lazytag,如果要访问该节点的子节点,仅需要下推到两个子节点
void push_down(int node)
{
tr[node<<1].sum+=(tr[node<<1].l-tr[node<<1].r+1)*tr[node].add;
tr[node<<1|1].sum+=(tr[node<<1|1].r-tr[node<<1|1].l+1)*tr[node].add;
tr[node<<1].add+=tr[node].add;
tr[node<<1|1].add+=tr[node].add;
tr[node].add=0;///传入后清0
}
以及区间更新函数
void update1(int l,int r,int x,int node)///区间更新
{
if(tr[node].l==l&&tr[node].r==r)
{
tr[node].sum+=(r-l+1)*x;
tr[node].add+=x;
return;
}
if(tr[node].add)
push_down(node);///如果有标记则下推
int mid=tr[node].l+tr[node].r>>1;
if(r<=mid)
update1(l,r,x,node<<1);
else if(l>mid) update1(l,r,x,node<<1|1);
else
{
update1(l,mid,x,node<<1);
update1(mid+1,r,x,node<<1|1);
}
tr[node].sum=tr[node<<1].sum+tr[node<<1|1].sum;
}
end