线段树
线段树的操作说实话比较灵活,写法也很多
比如往线段树里面插入一段值为c的区间[a, b]
void change(int p,int l,int r,int a,int b,int c) { if(a<=l&&b>=r) { val[p]=c; return; } int m=(l+r)>>1; if(a<=m) change(p<<1,l,m,a,b,c); if(b>m) change((p<<1)+1,m+1,r,a,b,c); val[p]=max(val[p<<1],val[(p<<1)+1]);//假设是维护最大值 } |
主程序里面调用change(1,1,n,a,b,c) 即可
当然,上面只是线段树的一种写法,还有一种写法如下(一看就知道两者的区别了)
void change(int p,int l,int r,int a,int b,int c) { if(a==l&&b==r) { val[p]=c; return; } int m=(l+r)>>1; if(b<=m) change(p<<1,l,m,a,b,c);//区间[a,b]全在左儿子树上 else if(a>m) change((p<<1)+1,m+1,r,a,b,c);//区间[a,b]全在右儿子树上 else{//区间[a,b]分为左右儿子两段 [a,m]和[m+1,b] change(p<<1,l,m,a,m,c); change((p<<1)+1,m+1,r,m+1,b,c); } } |
我个人比较喜欢前面第一种的写法,原因很简答,代码短
但是第二种也是必须的,为什么呢?
线段树可以动态修改值,看似是动态维护的,但是它的区间(也就是那棵树)是建好后就无法修改的,也就是“静态建树,动态维护”
那当我们遇到需要修改区间的问题该怎么办呢?我们可以维护每个区间的size(即当前区间中有多少个数),这样我们可以通过size来实现伪动态
算了,说不清楚,以SCOI2006的MinMax为例
http://blog.csdn.net/jiangzh7/article/details/8598061
对于这一题,第一种就显得有点力不从心了
那么查询呢?同样两种写法,这里给出第一种写法(同样是维护最大值)
int query(int p,int l,int r,int a,int b) { if(a<=l&&b>=r) return val[p]; int m=(l+r)>>1,x1=0,x2=0; if(a<=m) x1=query(p<<1,l,m,a,b); if(b>m) x2=query((p<<1)+1,m+1,r,a,b); return max(x1,x2); } |
我做过的线段树题目
【重庆省选2006】简单题easy http://blog.csdn.net/jiangzh7/article/details/8533051
Count the Colors (zoj 1610) http://blog.csdn.net/jiangzh7/article/details/8535203
弱点 weakness http://blog.csdn.net/jiangzh7/article/details/8559217
星星 POJ2352 http://blog.csdn.net/jiangzh7/article/details/8592676
Cover http://blog.csdn.net/jiangzh7/article/details/8587583
超市促销 rqnoj572http://blog.csdn.net/jiangzh7/article/details/8614323
动态最值 SCOI2006minmax http://blog.csdn.net/jiangzh7/article/details/8598061
序列操作operationSCOI2010【神目】http://blog.csdn.net/jiangzh7/article/details/8596173
小总结一下:
线段树这玩意儿能干的事很多,不过最近听说伸展树比它还要牛逼些,线段树能干的事伸展树都能干,最主要的是能动态修改区间,也就是可以完爆MinMax了!所以最近争取吧伸展树学学