pku3468 线段树 区间求和

A Simple Problem with Integers

又一道线段树,与前两道差异比较大,每次update操作中,是将一段区间的每一个值都加上某一个值,而不是简单的覆盖。

 
  
struct node{
int l, r, cover;
__int64 sum;
__int64 key;
}st[NN
* 8 ];

在这里,我用sum表示区间[l, r]的所有数的和,包括附加的值;key表示update中对区间[l, r]的附加值,cover表示此区间上有没有附加值。引用名词:当前区间指的是[st[id].l,st[id].r], 要查找的区间是[l, r], 代码中我总结到有5点值得我注意,值得我学习的地方:

key1:在初始化的时候,递归返回的时候将子区间的值加起来,保存到当前区间,作为初始化值。

key2:Update时,如果找到正好匹配的区间,将附加值累积,即可return,不用向下递归。

key3:Update时,如果不是正好匹配的区间,则将附加值加到当前区间的sum里,这就保证sum保存的是区间[l, r]的和,自然包括附加值,使得查找时可以快速返回。

key4:在Search时,要查找的区间总是包含于当前区间,所以查找过程中遇到的每一个附加值,都要累加起来。

key5:当找到要查找的区间时,也就是正好匹配,return ans + sum即可,ans为key4中累加的值,sum为当前区间的和。

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
#include < stdio.h >
#include
< stdlib.h >
#define NN 100000

struct node{
int l, r, cover;
__int64 sum;
__int64 key;
}st[NN
* 8 ];

__int64 f[NN
+ 2 ];

void Init( int l, int r, int id){
st[id].l
= l;
st[id].r
= r;
st[id].cover
= 0 ;
st[id].key
= 0 ;

if (r - l <= 1 ){
st[id].cover
= 1 ;
st[id].sum
= f[l];
return ;
}

int mid = (l + r) >> 1 ;

Init(l, mid, id
* 2 );
Init(mid, r, id
* 2 + 1 );
st[id].sum
= st[id * 2 ].sum + st[id * 2 + 1 ].sum; // key1
}

void Update( int l, int r, __int64 key, int id){

// key2
if (st[id].l == l && st[id].r == r){
st[id].key
+= key;
st[id].cover
= 1 ;
return ;
}
st[id].sum
+= (r - l) * key; // key3
/* if (st[id].cover > 0){
st[id * 2].cover = 1;
st[id * 2 + 1].cover = 1;
st[id * 2].key += st[id].key;
st[id * 2 + 1].key += st[id].key;
st[id].cover = 0;
st[id].key = 0;
}
*/
int mid = (st[id].l + st[id].r) >> 1 ;

if (r <= mid){
Update(l, r, key, id
* 2 );
}
else if (l >= mid){
Update(l, r, key, id
* 2 + 1 );
}
else {
Update(l, mid, key, id
* 2 );
Update(mid, r, key, id
* 2 + 1 );
}
}

__int64 Search(
int l, int r, int id){
__int64 ans
= 0 ;
// key4
if (st[id].cover > 0 ){
ans
+= st[id].key * (r - l);
}

// key5
if (st[id].l == l && st[id].r == r){
ans
+= st[id].sum;
return ans;
}

int mid = (st[id].l + st[id].r) >> 1 ;

if (r <= mid){
return ans + Search(l, r, id * 2 );
}
else if (l >= mid){
return ans + Search(l, r, id * 2 + 1 );
}
else {
return ans + Search(l, mid, id * 2 ) + Search(mid, r, id * 2 + 1 );
}
}
int main()
{
int N, Q, i, a, b;
__int64 c, ans;
char str[ 3 ];
scanf(
" %d%d " , & N, & Q);
for (i = 0 ; i < N; i ++ ){
scanf(
" %I64d " , & f[i]);
}
Init(
0 , N, 1 );
while (Q -- ){
scanf(
" %s " , str);
if (str[ 0 ] == ' Q ' ){
scanf(
" %d%d " , & a, & b);
ans
= Search(a - 1 , b, 1 );
printf(
" %I64d\n " , ans);
}
else {
scanf(
" %d%d%I64d " , & a, & b, & c);
Update(a
- 1 , b, c, 1 );
}
}
// system("pause");
return 0 ;
}

 

代码中我注释了一部分,开始我想错了,想把当前点的key值传到子区间,其实不用,用key2那种方法就行,看来,线段树千变万化,处理技巧很多啊,具体用什么方法,按题而定,学习了,注意总结常用线段树处理方法!

转载于:https://www.cnblogs.com/ylfdrib/archive/2010/07/18/1780070.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值