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