线段树模板
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;
}