第一次写线段树,感觉还是可以理解的(借鉴了coder_hsc的写法)
原文地址:http://blog.csdn.net/w00w12l/article/details/7920846
题目链接:http://poj.org/problem?id=3468
线段树为什么要开4倍空间:http://scinart.github.io/acm/2014/03/19/acm-segment-tree-space-analysis/
http://bbs.csdn.net/topics/380264561
题目大意:两种操作1.更新一段区间的值,即将一段区间上每个点都加上一个值。2.查询一段区间的和。
因操作数过大,故直接暴力是不行的。
建树操作:
lef,rig左右边界,sum和mark乘以区间长度,是lef到rig区间上的和,初始mark为0,递归建树,到叶子节点停止递归。
更新操作:
如果更新区间恰好相同,直接将更新值加到懒标记上,若不完全相同(因为查询至上而下,故区间必定包含更新区间),直接在该节点sum值上加上更新区间长度乘以更新的值。并根据更新区间与该区间的位置,递归继续更新,直至完全相符。
查询操作:
如果区间完全相符,返回sum和mark乘以区间长度之和 。若不完全相符,将懒标记下放,并在该区间sum值上加上懒标记乘以区间长度,并将mark值置0。并继续根据位置递归查询。
实现代码如下:
#include <iostream> #include <stdio.h> #define maxn 100010 using namespace std; struct node { int lef,rig,mid; //该节点的左右边界,中值 long long int sum,mark;//sum,mark乘区间之和为该区间总值,但sum不一定就是原值,会被更新 }Tree[maxn<<2];//一定要移两位,是有理论依据的,见上面链接,开3倍可能只是测试数据不够大,所以一直没事 int store[maxn];//存原序列 ,下标从1开始 void build_tree(int id,int lef,int rig) { int mid=(lef+rig)>>1; //初始化 Tree[id].lef=lef; Tree[id].rig=rig; Tree[id].mid=mid; Tree[id].mark=0; //到了叶子 if(lef==rig) { Tree[id].sum=store[lef];//lef刚好和原序列下标对应 return; } build_tree(id<<1,lef,mid); build_tree((id<<1)|1,mid+1,rig); //该节点左右树已建成 Tree[id].sum=(Tree[id<<1].sum+Tree[(id<<1)|1].sum); } void update(int id,int lef,int rig,int val) { //如果区间完全相符,直接更新mark if(Tree[id].lef==lef&&Tree[id].rig==rig) { Tree[id].mark+=val; return; } //若不完全相符,查询区间必定包含在该区间内,故直接更新该点sum //因为更新的区间在该区间内,所以sum值可以直接更新 Tree[id].sum+=(long long)val*(rig-lef+1); //完全落在mid左侧 if(Tree[id].mid>=rig) update(id<<1,lef,rig,val); //完全落在mid右侧 else if(Tree[id].mid<lef) update((id<<1)|1,lef,rig,val); //落在两侧 ,两侧都更新 else { update(id<<1,lef,Tree[id].mid,val); update((id<<1)|1,Tree[id].mid+1,rig,val); } } long long int query(int id,int lef,int rig) { //如果区间完全相符,返回sum和mark之和 if(Tree[id].lef==lef&&Tree[id].rig==rig) return Tree[id].sum+Tree[id].mark*(long long)(rig-lef+1); //区间不完全相符,但查询区间一定是从大到小,故需下放懒标记 if(Tree[id].mark!=0) { //将mark值放入sum中,mark清空,避免二次赋值mark给子树 //该节点区间大小大于查询区间大小 Tree[id<<1].mark+=Tree[id].mark; Tree[(id<<1)|1].mark+=Tree[id].mark; Tree[id].sum+=(long long)(Tree[id].rig-Tree[id].lef+1)*Tree[id].mark; Tree[id].mark=0; } //完全在左侧 if(Tree[id].mid>=rig) return query(id<<1,lef,rig); //在右侧 else if(Tree[id].mid<lef) return query((id<<1)|1,lef,rig); //返回两边之和 else return query(id<<1,lef,Tree[id].mid)+query((id<<1)|1,Tree[id].mid+1,rig); } int main() { int n,m,l,r,v; char c; while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;i++) { scanf("%d",&store[i]); } build_tree(1,1,n); for(int i=0;i<m;i++) { getchar(); scanf("%c",&c); if(c=='Q') { scanf("%d%d",&l,&r); printf("%lld\n",query(1,l,r)); } else { scanf("%d%d%d",&l,&r,&v); update(1,l,r,v); } } } }