线段树整理

终于学完了线段树,没学之前觉得特牛x,看了好几位dalao的博客,就整理一下,增强理解记忆ovo。

一、引入

线段树是一个数据结构。数据结构是先有需求,后有数据结构,用来将数据进行处理,达到优化的目的。(闵学长讲题步骤:先暴力,后用数据结构优化)。

问题:

给出n个数,n<=**,和m个询问,每次询问区间[l,r]的和,并输出。

一般思路,先暴力。就挨个枚举呗。可是当**很大的时候呢,就容易超时。这时我们就有了需求,需要想一个数据结构来优化我们的算法。

就有了线段树这个数据结构。

二、概念

什么是线段树呢?顾名思义,树上存着线段呗。以前我们树上的每一个结点存的是权值,现在,我们的每一个结点上存的是每一个区间的信息。

线段树是一个高效处理动态区间的查询问题。线段树的主要思想是二分。

如图,我们要对区间0--5进行一系列访问,结点1存0--5区间的信息,然后将这个区间二分为0--2和3--5,它的左孩子存0--2区间的信息,右孩子存3--5区间的信息。

依次这样推下去。线段树的几种用法:建树 单点查询 单点修改 区间查询 区间修改 

三、用法

(1)建树:

用一个结构体 存每一个结点存的区间的左端点和右端点 叶子结点的val存其本身的的值 不是叶子结点存其区间的和。

建树的过程是一个递归的过程。

//构建线段树 
const int maxn=1000;
struct segTreenode
{
    int val,l,r,s;
}segTree[maxn*4];//大小*4 先这么记吧 具体我也没搞懂
void build(int root,int l,int r)//线段树的当前结点和当前结点的左右区间 
{
    segTree[root].l=l;segTree[root].r=r; 
    if(segTree[root].l==segTree[root].r)//是叶子结点 
    {
        scanf("%d",&segTree[root].val);
        return;//不必递归 
    }
      int m=(l+r)>>1;//区间的中间值 
      build(root*2,l,m);//建左子树 
      build(root*2+1,m+1,r);//建右子树 
      segTree[root].val=segTree[root*2].val+segTree[root*2+1].val;//当前值为左右子树的和 
}

(2)单点查询

思想和二分找一个数差不多 只是看看这个数在当前点的左子树还是右子树

//询问单个点的状态; 
void ask(int k)
{
    if(segTree[k].l==segTree[k].r)//是叶子结点
    {
        ans=segTree[k].val;
        return;//一定要return 不必递归
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)ask(k*2);//找左子树
    if(x>m)ask(k*2+1);//找右子树
}

(3)修改单点

//修改单点
void change_point(int k)
{
    if(segTree[k].l==segTree[k].r)//找到叶子
    {
        segTree[k].val+=w;
        return;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)change_point(k*2);
    else change_point(k*2+1);
    segTree[k].val=segTree[k*2].val+segTree[k*2+1].val;//修改val,因为区间和已经改变

(4)区间查询 

求一个区间的和 只是将他的子区间的和加起来

//区间查询
void  ask_all(int k)
{
    if(segTree[k].l>=x&&segTree[k].r<=y)
    {
        ans+=segTree[k].val;
        return;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)ask_all(k*2);//这两行容易出错
    if(y>m)ask_all(k*2+1); 
}

(5)求区间最小值 建树过程

//线段树查询区间最小值
void build(int root,int l,int r)
{
    if(segTree[root].l==segTree[root].r)
    {
        scanf("%d",&segTree[root].val);
        return;
    }
    int m=(segTree[root].l+segTree[root].r)>>1;
    build(root*2,l,m);
    build(root*2+1,m+1,r);
    segTree[root].val=min(segTree[root*2].val,segTree[root*2+1].val);//val从存和改为存其区间的最小值
}

(6)求区间最小值

//线段树查询区间最小值 
int ask_minn(int k)
{
    if(segTree[k].l==segTree[k].r)
    {
        return segTree[k].val;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)ask_minn(k*2);
    if(y>m)ask_minn(k*2+1);
}

(7)更新结点 修改最小值

//线段树最小值结点更新
void change_point(int k)
{
    if(segTree[k].l==segTree[k].r)
    {
        segTree[k].val=x;
        return;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)change_point(k*2);
    else change_point(k*2+1);
    segTree[k].val=min(segTree[k*2].val,segTree[k*2+1].val);
 } 

(8)懒标记下传

//懒标记下传 
void down(int k)
{
    segTree[k*2].s+=segTree[k].s;
    segTree[k*2+1].s+=segTree[k].s;
    segTree[k*2].val=segTree[k].s*(segTree[k*2].r-segTree[k*2].l+1);
    segTree[k*2+1].val=segTree[k].s*(segTree[k*2+1].r-segTree[k*2+1].l+1);
    segTree[k].s=0;
}

(9)线段树区间更新‘

//线段树区间更新
void change_sed(int k)
{
    if(segTree[k].l>=x&&segTree[k].r<=y)
    {
        segTree[k].val+=x;
        segTree[k].s=x;
        return;
    }
    if(segTree[k].s)down(k);
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)change_sed(k*2);
    else change_sed(k*2+1);
}

四、线段树练习

(1)http://codevs.cn/problem/1080/

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100001;
struct segTreenode
{
    int l,r,val,s;
}segTree[maxn*4];
int n,m,od,x,y,pos,add,ans;
inline void build(int root,int l,int r)
{
    segTree[root].l=l;segTree[root].r=r;
    if(l==r)
    {
        scanf("%d",&segTree[root].val);
        return;
    }
    int m=(l+r)>>1;
    build(root*2,l,m);
    build(root*2+1,m+1,r);
    segTree[root].val=segTree[root*2].val+segTree[root*2+1].val;
}
inline void change_point(int k)
{
    if(segTree[k].l==segTree[k].r)
    {
        segTree[k].val+=add;
        return;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(pos<=m)change_point(k*2);
    else change_point(k*2+1);
    segTree[k].val=segTree[k*2].val+segTree[k*2+1].val;
}
inline void ask_sed(int k)
{
    if(segTree[k].l>=x&&segTree[k].r<=y)
    {
        ans+=segTree[k].val;
        return;
    }
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)ask_sed(k*2);
    if(y>m)ask_sed(k*2+1);
}
int main()
{
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        ans=0;
        scanf("%d",&od);
        if(od==1)
        {
            scanf("%d%d",&pos,&add);
            change_point(1);
        }
      if(od==2)
        {
            scanf("%d%d",&x,&y);
            ask_sed(1);
            printf("%d
",ans);
        }
    }
    return 0;
}
View Code

(2)http://codevs.cn/problem/1081/

 #include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100001;
struct segTreenote
{
    int l,r,val,s;
}segTree[maxn*4];
int n,q,od,pos,x,y,z;
inline void build(int root,int l,int r)
{
    segTree[root].l=l;segTree[root].r=r;
    if(l==r)
    {
        scanf("%d",&segTree[root].val);
        return;
    }
    int m=(l+r)>>1;
    build(root*2,l,m);
    build(root*2+1,m+1,r);
    segTree[root].val=segTree[root*2].val+segTree[root*2+1].val;
}
inline void down(int k)
{
    segTree[k*2].s+=segTree[k].s;
    segTree[k*2+1].s+=segTree[k].s;
    segTree[k*2].val+=segTree[k].s*(segTree[k*2].r-segTree[k*2].l+1);//.+
    segTree[k*2+1].val+=segTree[k].s*(segTree[k*2+1].r-segTree[k*2+1].l+1);
    segTree[k].s=0;
}
inline void add(int k)
{
    if(segTree[k].l>=x&&segTree[k].r<=y)
    {
        segTree[k].val+=(segTree[k].r-segTree[k].l+1)*z;
        segTree[k].s+=z;
        return;
    }
    if(segTree[k].s)down(k);
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(x<=m)add(k*2);
    if(y>m) add(k*2+1);//
    segTree[k].val=segTree[k*2].val+segTree[k*2+1].val;
}
inline int ask(int k)
{
    if(segTree[k].l==segTree[k].r)
    {
        return segTree[k].val;
    }
    if(segTree[k].s)down(k);
    int m=(segTree[k].l+segTree[k].r)>>1;
    if(pos<=m)ask(k*2);
    else ask(k*2+1);
}
int main()
{
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&od);
        if(od==1)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(1);
        }
        if(od==2)
        {
            scanf("%d",&pos);
            printf("%d
",ask(1));
        }
    }
    return 0;
 } 
View Code

呼~整理完了

原文地址:https://www.cnblogs.com/zzyh/p/6798733.html