给定一个数组,进行m次操作,将第x个元素的值替换为y,进行m次操作,求第L个元素到第R个元素的之间所有元素的和(包括L和R)
#include <algorithm>
#include <iostream>
using namespace std;
long num[100001];
long tree[100000000];
void build_tree(long num[],long tree[],long node,long left,long right){//创建线段树
if(left==right){//可以确定这个地方就是那一个数字的和
tree[node] = num[left];
}else{
long left_node = 2 * node + 1;
long right_node = 2 * node + 2;
long mid = (left + right)/2;
//分段求
build_tree(num,tree,left_node,left,mid);//创建并求出当前节点的左子节点
build_tree(num,tree,right_node,mid+1,right);//同理,计算当前节点的右子节点的值
tree[node] = tree[left_node] + tree[right_node];//这个节点是它的左子节点和右子节点的和
}
}
//把原数组下标为need的数字的值改为val
void exchange_tree(long num[],long tree[],long node,long left,long right,long need,long val){
//node 是tree中当前节点的下标,left,right是当前所选的区间的左右端点
if(left==right){//原下标need的位置找到了,换值
tree[node] = val;
num[need] = val;
}else{
long left_node = 2 * node + 1;//改节点的左子节点,代表从left加到mid的值
long right_node = 2 * node + 2;//同理,代表mid+1到right的值
long mid = (left + right)/2;
if(need>=left&&need<=mid){//说明在左边的那一半,将节点换到当前节点的左节点,查询当前区间的左半边
exchange_tree(num,tree,left_node,left,mid,need,val);
}else{//在mid的右边到结束,同理,当前节点切换到当前节点的右节点
exchange_tree(num,tree,right_node,mid+1,right,need,val);
}
tree[node] = tree[left_node] + tree[right_node];//这个节点是它的左子节点和右子节点的和
} //也就是更新子节点之后将它的父节点顺带更新
}
//L到R区间求和
long sum_tree(long num[],long tree[],long node,long left,long right,long L,long R){
//left,right是当前区间的左右端点,递归后按照当前区间中点分为左右两边继续递归
//L,R是所求区间的左右端点
if(left>R||right<L){//区间的右边小于L或者左边大于R,二者交集为空,直接return 0
return 0;
}
else if(left>=L&&right<=R){//当前区间包含于[L,R]区间,直接返回这个区间的和的值,不必深入递归
return tree[node];
}
else if(left==right){//找到左右区间相同,因此L到R包含这个数字,返回这个叶节点
return tree[node];
}
else{
long left_node = 2 * node + 1;
long right_node = 2 * node + 2;
long mid = (left + right)/2;
long left_sum = sum_tree(num,tree,left_node,left,mid,L,R);//求区间左边含有L到R区间的和
long right_sum = sum_tree(num,tree,right_node,mid+1,right,L,R);
return left_sum + right_sum;
}
}
int main()
{
long n = 0;
cin >> n;
for(long i = 0;i<n;i++){
scanf("%ld",&num[i]);
}//将n个数字输入进初始数组
build_tree(num,tree,0,0,n-1);//将初始数组对应的线段树建立出来
long m = 0;
cin >> m;//进行m次操作,每次操作对原数组其中一个数字更改
long L = 0,R = 0;
for(long i = 0;i<m;i++){
long x,y;
scanf("%ld %ld",&x,&y);//输入的是第几个元素替换成另一个数,不是输入的下标
printf("修改之前第%d个元素为%d\n",x,num[x]);
exchange_tree(num,tree,0,0,n-1,x-1,y-1);//从0开始,数组下标从0到n-1,将原数组下标为x-1的值换为y-1
printf("修改之后第%d个元素为%d\n",x,num[x]);
}
cin >> m;
for(long i = 0;i<m;i++){
long x,y;
scanf("%ld %ld",&x,&y);//x和y是第几个元素,不是下标
if(x>y){//不确定输入的x和y谁大谁小
L = y - 1;
R = x - 1;
}else{
L = x - 1;
R = y - 1;
}
printf("%ld\n",sum_tree(num,tree,0,0,n-1,L,R));//求L到R的区间和
}
return 0;
}
不修改元素,测试区间和
对数组元素进行修改并测试区间和