线段树(区间树)

  • 线段树

  1. 定义及作用

  2. 建树

  3. 基本操作

 一.定义及作用

线段树是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N。

二.建树

1.先定义一个结构体以便每个节点能存东西

struct node
{
    int l;//区间左端点
    int r;//区间右端点
    int w;//区间保存的值
}tree[400001];//开四倍大小

2.递归建树

void build(int k,int l,int r)//建树 
{
    tree[k].l=l,tree[k].r=r;
    if(tree[k].l==tree[k].r)                   //递到叶子节点的时候归
    {
        scanf("%d",&tree[k].w);                //输入各叶子节点的值
        return;                                //出递归
    }
    int m=(l+r)/2;                             //准备生成左右子树
    build(k*2,l,m);                            //递归左子树
    build(k*2+1,m+1,r);                        //左子树递归完递归右子树
    tree[k].w=tree[k*2].w+tree[k*2+1].w;       //向上更新各区间值就是pushup(k)
}

三、线段树的基本操作

向上更新

void pushup(int k)
{
    tree[k].w=tree[k*2].w+tree[k*2+1].w;    //父亲的值根据其子孙的值确定
}

向下更新

void pushdown(int k)
{
    tree[k*2].f+=tree[k].f;                                      //将更改的单位值传递给左儿子
    tree[k*2+1].f+=tree[k].f;                                    //将更改的单位值传递给右儿子
    tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);          //更新左儿子保存的值
    tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);    //更新右儿子保存的值
    tree[k].f=0;                                                 //向下更新完毕取消标记值
}

1.单点查询

void ask_point(int k,int x)
{
    if(tree[k].l==tree[k].r)                 //当查到叶子节点时
    {    
        ans=tree[k].w;                       //ans为全局变量传递查询的值
        return ;                             //出递归   
    }
    if(tree[k].f) pushdown(k);               //向下更新
    int m=(tree[k].l+tree[k].r)/2;           //取区间中值,准备递归左右子树查询
    if(x<=m) ask_point(k*2);                 //若查询的单点为区间中值或在区间左边则递归左子树
    else ask_point(k*2+1);                   //否则递归右子树
}

2.单点更新

void change_point(int k,int x,int v)
{
    if(tree[k].l==tree[k].r)
    {
        tree[k].w+=v;
        return;
    }
    if(tree[k].f) pushdown(k);
    int m=(tree[k].l+tree[k].r)/2;
    if(x<=m) change_point(k*2);
    else change_point(k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

3.区间查询

void ask_interval(int k,int a,int b)
{
    if(tree[k].l>=a&&tree[k].r<=b)                 //区间在a,b之间
    {
        ans+=tree[k].w;
        return;
    }
    if(tree[k].f) pushdown(k);
    int m=(tree[k].l+tree[k].r)/2;
    if(a<=m) ask_interval(k*2);              //若本区间中值等于a或在a右边(说明左边还能递归)
    if(b>m) ask_interval(k*2+1);             //若本区间中值在b左边(说明右边还能递归)    
}

4.区间更新(未解释懒标记)

void change_interval(int k)
{
    if(tree[k].l>=a&&tree[k].r<=b)
    {
        tree[k].w+=(tree[k].r-tree[k].l+1)*y;
        tree[k].f+=y;
        return;
    }
    if(tree[k].f) pushdown(k);
    int m=(tree[k].l+tree[k].r)/2;
    if(a<=m) change_interval(k*2);
    if(b>m) change_interval(k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

猜你喜欢

转载自blog.csdn.net/TernYoung/article/details/81367179