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为当前区间的和。

 

代码
#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那种方法就行,看来,线段树千变万化,处理技巧很多啊,具体用什么方法,按题而定,学习了,注意总结常用线段树处理方法!

posted on 2010-07-18 11:55  ylfdrib  阅读(1812)  评论(0编辑  收藏  举报