终于学完了线段树,没学之前觉得特牛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; }
(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; }
呼~整理完了