A Simple Problem with Integers-线段树
update@2011-08-01
struct node{
int left,right;
long long sum,weight;
}tree[upper_bound];
int A[upper_bound];//upper_bound_n
void build(int i,int a,int b){
//tree[i].left tree[i].right为节点i的左右边界
if(a != b){
tree[i].left = a;tree[i].right= b;
build(i<<1,a,(a+b)>>1);//递归构造左子树
build((i<<1)+1,((a+b)>>1)+1,b);//递归构造右子树
tree[i].sum = tree[i<<1].sum + tree[(i<<1)+1].sum;//回溯 左右子树的和等于父节点
}
else{
tree[i].left = tree[i].right = a;
tree[i].sum = A[a];
}
tree[i].weight = 0;
}
int add(int i,int a,int b,int v){
if(tree[i].left == a && tree[i].right == b){
//恰好是这个区间
//如果遍历到叶子节点再加的话会超时的
tree[i].weight += v;//将这个值保存而不是直接累加
return 1;
}
else{
//不是该区间但是属于该区间
tree[i].sum += v*(b - a + 1);
//之前写成 tree[i].sum += (tree[i].right - tree[i].left + 1)*v WA了好多次
}
int mid = (tree[i].left+tree[i].right) >> 1;
if(a <= mid && b > mid){
add(i<<1,a,mid,v);
add((i<<1)+1,mid+1,b,v);
}
else{
add((i<<1)+(b<=mid?0:1),a,b,v);
}
return 1;
}
long long query(int i,int a,int b){
int mid = (tree[i].left+tree[i].right) >> 1;
if(tree[i].left == a && tree[i].right == b){
return tree[i].sum + (tree[i].right - tree[i].left + 1)*tree[i].weight;
}
else if(tree[i].weight != 0){
tree[i].sum += (tree[i].right - tree[i].left + 1)*tree[i].weight;//首先更新自己
tree[i<<1].weight += tree[i].weight;//然后传递给左右子树
tree[(i<<1)+1].weight += tree[i].weight;
tree[i].weight = 0;//自身清零
}
if(a <= mid && b > mid){
return query(i<<1,a,mid) + query((i<<1)+1,mid+1,b);
}
return query((i<<1) + (b<=mid?0:1),a,b);
}
题目链接: A Simple Problem with Integers
一道很简单的线段树 YY了很长时间,开始add函数遍历到了叶子节点 超了,之后改成先记录下增加的值,当需要查询该区间时再更新该区间就可以了。还有可以优化的地方,比如查询区间[2,5],可以用[1,5] - [1,1] ,这样对左右子树的查询可以缩减到对左子树的查询