线段树的写法,主要就分三大部分,也就是三个函数:建树(build)、操作(update),以及询问(query)。
此代码为区间修改,区间查询。
首先建树:
void build(int L,int R,int now) { l[now]=L; r[now]=R; //lazy[now]=0; if(L==R) { scanf("%lld",&sum[now]); return ; } int mid=(L+R)/2; build(L,mid,now*2); build(mid+1,R,now*2+1); sum[now]=sum[now*2]+sum[now*2+1]; }
每一次将已有区间分为两部分,直到区间中剩下一个元素(为研究方便,假装存在这样的区间)。对于判断,只要(L==R)成立即可,此时便直接赋值,并返回。(l[]为now编号的区间的最左端的元素值,r[]为最右端元素值)
那么对于修改:
void update(int L,int R,ll d,int now) { if(l[now]==L&&r[now]==R) { lazy[now]+=d; return ; } sum[now]+=(long long)d*(R-L+1); int mid=(l[now]+r[now])/2; if(R<=mid) update(L,R,d,now*2); else if(mid<L) update(L,R,d,now*2+1); else { update(L,mid,d,now*2); update(mid+1,R,d,now*2+1); } }
lazy数组是用来提高代码的效率,即当搜到应该修改的区间时(假设这个节点的序号为now),暂时不修改其sum[](记录值的数组),而是lazy[now]+=d(注意是+=不是=,我错了好几次),并且不再修改在now下方的节点。那么对于now上方的所有点,就都要加上我要修改的数的总数(即R-L)和我要增加的数的乘积(应该很好理解)。
如果修改的区间在两个分支,那么我们分开继续搜就可以了,搜到了就操作lazy并且返回即可。
注意:
1.此时的R,L为将要修改的区间的两个端点,所以sum[]+=(R-L)*d。
2.找到对应区间后,并没有修改sum[]值,所以以后再次碰到lazy的时候要修改其sum[],并且lazy下传(后话一会儿再说)。
3.对于区间的平分问题:是要将整个区间平分,所以mid=(l[now]+r[now])/2,而不是R和L(对于每一个mid的求值,操作的都应是l[now]和r[now])。
最后是求和:(加油马上就完事了~>\<)
ll query(int L,int R,int now) { sum[now]+=(long long)(r[now]-l[now]+1)*lazy[now]; lazy[now*2]+=lazy[now]; lazy[now*2+1]+=lazy[now]; lazy[now]=0; if(l[now]==L&&r[now]==R) { return sum[now]; } int mid=(l[now]+r[now])/2; if(R<=mid) return query(L,R,now*2); else if(mid<L) return query(L,R,now*2+1); else return query(L,mid,now*2)+query(mid+1,R,now*2+1); }
对于每一个搜到的点,都要加上它的lazy(反正没有lazy就是+0,没影响的),并且注意(敲黑板了):所有有lazy数组的点,它下面的点或多或少都会有本该操作却没有操作的点,它们本该加上的值就被记录在了现在搜到的这个点里,所以我们要将lazy数组下传(并且清零!)。
注意:
1.于lazy的下传:因为lazy存在说明这个点的整个区间都需要修改,所以为sum[]+=lazy[now]*(r[now]-l[now]+1),而不是R和L,它们只是我们要求和的点罢了,碰到它们,直接返回sum[]就可以。
2.碰到一个sum[],即使有lazy也要(更要)修改其sum,因为之前的修改函数,对于正好的区间,是只记录了lazy而没修改sum。
好啦好啦,该说的都说了,手也累了,希望能帮到大家!
最后还是完整代码:
#include<cstdio> using namespace std; #define maxn 100005 #define ll long long int l[4*maxn],r[4*maxn]; ll lazy[8*maxn],sum[4*maxn]; void build(int L,int R,int now) { l[now]=L; r[now]=R; //lazy[now]=0; if(L==R) { scanf("%lld",&sum[now]); return ; } int mid=(L+R)/2; build(L,mid,now*2); build(mid+1,R,now*2+1); sum[now]=sum[now*2]+sum[now*2+1]; } void update(int L,int R,ll d,int now) { if(l[now]==L&&r[now]==R) { lazy[now]+=d; return ; } sum[now]+=(long long)d*(R-L+1); int mid=(l[now]+r[now])/2; if(R<=mid) update(L,R,d,now*2); else if(mid<L) update(L,R,d,now*2+1); else { update(L,mid,d,now*2); update(mid+1,R,d,now*2+1); } } ll query(int L,int R,int now) { sum[now]+=(long long)(r[now]-l[now]+1)*lazy[now]; lazy[now*2]+=lazy[now]; lazy[now*2+1]+=lazy[now]; lazy[now]=0; if(l[now]==L&&r[now]==R) { return sum[now]; } int mid=(l[now]+r[now])/2; if(R<=mid) return query(L,R,now*2); else if(mid<L) return query(L,R,now*2+1); else return query(L,mid,now*2)+query(mid+1,R,now*2+1); } int main() { int n,m; scanf("%d%d",&n,&m); build(1,n,1); while(m--) { int op,a,b; scanf("%d%d%d",&op,&a,&b); if(op==1) { ll x; scanf("%lld",&x); update(a,b,x,1); } else printf("%lld ",query(a,b,1)); } return 0; }