poj3468
区间增减值,区间和。
线段树思想:利用二叉树将每一个区间所需要的值都记录下来。
lazy思想:延迟对叶子节点的修改,要用到的时候才真的去修改,lazy数组记录修改的值。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 1e6+10; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 ll a[maxn]; ll d[maxn<<2]; ll lazy[maxn<<2]; void PushDown(ll rt,ll l,ll r) //下放懒惰标记 { if(lazy[rt]) { lazy[rt<<1] +=lazy[rt]; //左儿子懒惰标记加上当前父亲的懒惰值 lazy[rt<<1|1]+=lazy[rt]; //右儿子懒惰标记加上当前父亲的懒惰值 ll m=(l+r)>>1; d[rt<<1]+=lazy[rt]*(m-l+1); //左儿子的区间和加上父亲的懒惰标记值乘区间长度 d[rt<<1|1]+=lazy[rt]*(r-m); //右儿子的区间和加上父亲的懒惰标记值乘区间长度 lazy[rt] = 0; //父亲懒惰值置0,懒惰标记下放完成 } } void build(ll l,ll r,ll rt) { //len[rt]=l-r+1; 可以用一个数组纪录每个节点对应的区间长度 if(l==r) { d[rt]=a[l];return; } ll m=(l+r)>>1; build(lson); build(rson); d[rt]=d[rt<<1]+d[rt<<1|1]; //求和,可相应的改为max min等函数 } void update(ll L,ll R,ll l,ll r,ll rt,ll val)//[L,R]目标区间 [l,r]当前区间 { if(L<=l && r<=R) //如果当前区间在目标区间内 { lazy[rt]+=val; //打上懒惰标记 d[rt]+=(r-l+1)*val; return; //区间和加上区间长度乘val } PushDown(rt,l,r); //下放懒惰标记 ll m=(l+r)>>1; if(m>=L) update(L,R,lson,val); //[l,L,m,r] //说明与左儿子有交集 if(R>m) update(L,R,rson,val); //[l,m,R,r] //说明与右儿子有交集 d[rt]=d[lson]+d[rson]; //向上更新 } ll query(ll L,ll R,ll l,ll r,ll rt) { if(L<=l && r<=R) { return d[rt]; } PushDown(rt,l,r); ll m=(l+r)>>1; ll res=0; if(L <= m) res += query(L,R,lson); if(R > m) res += query(L,R,rson); return res; } int main() { ll N,Q; scanf("%lld%lld",&N,&Q); for(int i=1;i<=N;i++) scanf("%lld",&a[i]); build(1,N,1); char ope[5]; ll l,r,val; while(Q--) { scanf("%s",ope); if(ope[0]=='Q') { scanf("%lld%lld",&l,&r); printf("%lld\n",query(l,r,1,N,1)); } else { scanf("%lld%lld%lld",&l,&r,&val); update(l,r,1,N,1,val); } } return 0; }