初学线 c2f2 段树总结
2016-08-01 15:53
85 查看
线段树善于解决区间最值,和,合并等问题,运用很广泛,下面是一些总结:(自我感觉错误和不足之处有点多啊
)
)
单点更新模版: #define MAXN 100005 struct node { int l,r,mi,mx,sum;//表示线段[l,r],中的最小、大值,及和值 }tr[MAXN*4]; int a[MAXN]; void build(int id,int l,int r) { tr[id].l=l; tr[id].r=r; if(l==r)//叶子节点 { tr[id].mx=tr[id].mi=tr[id].sum=a[l]; } else { int mid=(l+r)/2; build(id*2,l,mid);//往左儿子建树 build(id*2+1,mid+1,r);//往右儿子建树 //对于编号为id的节点(线段),它左右儿子都建完了 //接下来就是更新它自身的值,即合并它左右儿子。 tr[id].mx=max(tr[id*2].mx,tr[id*2+1].mx); tr[id].mi=min(tr[id*2].mi,tr[id*2+1].mi); tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; } } int quary(int id,int l,int r)//查询[l,r]区间的最小值、最大值、和值 { if(l<=tr[id].l&&tr[id].r<=r) //如果编号为id的节点(线段)被[l,r]区间覆盖了,则直接返回此线段的结果 { //return tr[id].mi; //return tr[id].mx; return tr[id].sum; } else { int mid=(tr[id].l+tr[id].r)/2; if(r<=mid)return quary(id*2,l,r);//r<=mid 说明我们要找的区间在左边 else if(l>mid)return quary(id*2+1,l,r);//l>mid 我们要找的区间在左边 else//之间 { int x=quary(id*2,l,r);//先找左边 int y=quary(id*2+1,l,r);//再找右边 //最后在合并,返回 //return min(x,y); // return max(x,y); return x+y; } } } void update(int id,int pos,int val)//单点更新,把a[pos]修改成val { if(tr[id].l==tr[id].r) { tr[id].mi=tr[id].mx=tr[id].sum=val; } else { int mid=(tr[id].l+tr[id].r)/2; if(pos<=mid)update(id*2,pos,val); else update(id*2+1,pos,val); //同样的道理,回溯的时候有两个儿子节点更新id //tr[id].mx=max(tr[id*2].mx,tr[id*2+1].mx); //tr[id].mi=min(tr[id*2].mi,tr[id*2+1].mi); tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; } } 区间更新模版: #define MAXN 100010 int a[MAXN]; struct node { int l,r,mi,mx; long long int sum,sign;//long long 根据实际情况而定 }tr[MAXN*4]; void build(int id,int l,int r) { tr[id].l=l; tr[id].r=r; tr[id].sign=0;//区间更新初始化 if(l==r) tr[id].sum=a[l];//tr.[id].mi=tr[id].mx=tr[id].sum=a[l] else { int mid=(l+r)/2; build(id*2,l,mid); build(id*2+1,mid+1,r); tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; //tr[id].mx=max(tr[id*2].mx,tr[id*2+1].mx); //tr[id].mi=min(tr[id*2].mi,tr[id*2+1].mi); } } void add(int id,int l,int r,long long int val) { if(l==tr[id].l&&tr[id].r==r) tr[id].sign+=val;//标记要加的值 else { //tr[id].ma+=val; //tr[id].mi-=val; tr[id].sum+=val*(r-l+1);//注意,这一步极其重要,这是找到你要标记区间的前面的大区间的维护,因为以后都是向下更新,所以上面的要提前更新 int mid=(tr[id].l+tr[id].r)/2; if(r<=mid) add(id*2,l,r,val); else if (l>mid) add(id*2+1,l,r,val); else { add(id*2,l,mid,val); add(id*2+1,mid+1,r,val); } } } long long int query(int id,int l,int r)//long long 视情况而定 { if(l==tr[id].l&&tr[id].r==r) return tr[id].sum+(r-l+1)*tr[id].sign; //tr[id].ma+=tr[id].sign; //tr[id].mi-=tr[id].sign; else { tr[id].sum+=(tr[id].r-tr[id].l+1)*tr[id].sign;//对所到区间都跟新,不管有没有标记,其实也可以判断一下,在看看更新与否 int mid=(tr[id].l+tr[id].r)/2; add(id*2,tr[id].l,mid,tr[id].sign);//向下更新sign标记,若要向下求,就要向下更新 add(id*2+1,mid+1,tr[id].r,tr[id].sign); tr[id].sign=0; if(r<=mid) return query(id*2,l,r); else if(l>mid) return query(id*2+1,l,r); else return query(id*2,l,mid)+query(id*2+1,mid+1,r); } } 以上的这个区间更新版本,跟雨神讲的有点出入,即要一直更新到恰好要标记的地方,上面的大区间值都要更新,而他们是最后查询才更新,再回溯上去 注意这个区间更新版本:(加了lazy,直接在这里面判断是否要向下更新)因为此题查询查询整个区间 void add(int id,int l,int r,int val) { if(l==tr[id].l&&tr[id].r==r) { tr[id].lazy=1; tr[id].sign=val; tr[id].sum+=(r-l+1)*val; } else { int mid=(tr[id].l+tr[id].r)/2; if(tr[id].lazy==1) { add(id*2,tr[id].l,mid,tr[id].sign);//即这里,上面模版是在query中再往下更新,其实都差不多 add(id*2+1,mid+1,tr[id].r,tr[id].sign); tr[id].sign=0; tr[id].lazy=0; } if(r<=mid) add(id*2,l,r,val); else if (l>mid) add(id*2+1,l,r,val); else { add(id*2,l,mid,val); add(id*2+1,mid+1,r,val); } tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; } }
相关文章推荐
- 【总结】段树完整版(未完成,不断更新)
- JAVA与C++::关于JNI中文字符串操作问题总结
- XP方法学习总结及对小组开发的思考
- JDBC与字符集总结
- VC常见入门问题总结(一)
- GeoTiff探索成果总结
- 侯捷专栏:长吁一口气(三年大陆经验总结)
- 程序设计和调试中的几点总结
- MASM宏使用总结
- JIURL PE 格式学习总结(一)-- PE文件概述
- SCI软件使用方法总结-定位测量篇
- JIURL PE 格式学习总结(二)-- PE文件中的输出函数
- JIURL PE 格式学习总结(三)-- PE文件中的输入函数
- JIURL PE 格式学习总结(四)-- PE文件中的资源
- 总结:在一个工程中添加别的工程的中已创建好的对话框资源。(或是。。。)
- .NET环境下水晶报表使用总结
- 精彩总结:★加密网页破解大法★
- duwamish7的一些代码分析总结
- 没头没尾--项目开发笔记:项目问题的阶段性总结,下一步…………
- DW属性设置的总结