第一次写线段树,感觉还是可以理解的(借鉴了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);
}
}
}
}