蒟蒻的线段树(目前是略讲,之后完善)

蒟蒻的线段树

蒟蒻曾经就学过线段树,但是最近才弄懂(惭愧
线段树,初次听闻这样一个高大上的名字,以为是解决什么惨绝人寰的问题
没想到,竟然只是处理区间的。。。
当时我小小的脑袋充满大大的疑惑
处理区间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倍(能用就行)
蒟蒻的线段树(目前是略讲,之后完善)_第1张图片接下来就是如何建立这个线段树

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

你可能感兴趣的:(蒟蒻的线段树(目前是略讲,之后完善))