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