这道题题意很简单就是如果字符为Q那么就询问[i,j]区间的和,如果为U,那么就更新i点值为j;
线段树基础题目;
那什么是线段树?
我自己是这样理解的:
线段是本质上就是一个区间的映射为一个节点+二叉树的后序遍历;著名的RMQ问题就是很基础的线段树操作;
几何理解:
可以发现线段树就是把区间分块,然后存下对应区间的问题规模(比如这个区间Max或者Min或者Sum或者平均值。。等等);
可以很明显发现这个二叉树的子叶节点就是L==R(这里要很明白,因为对后面的递归很有帮助!!)
所以这个问题就很容易了,我只需要把问题规模存下来,然后递归一遍就OK了(如果你对二叉树的三种遍历方式很熟悉,理解这个线段树的递归就很容易了);
线段树的本质就是这个了;
但是如何Build和操作呢?
建树时利用二叉树的后序遍历+Tree来循序存储线段树的节点对应的值;
意思就是:
可以很明显发现子节点和父节点的index的关系;所以利用这个关系我就可以Build了;
也就是(这里就建立好了线段树的逻辑存储结构了):
void Build(int root,int L,int R){//建树
if(L==R){//当左端点==有端点时说明到了叶结点
scanf("%d",&Tree[root]);//根据节点编号输入;
//cout<<":"<<Tree[L]<<" ";
return;
}else{
int mid=(L+R)/2;//二叉树的后续遍历用来建立树,至于为什么看看书就可以理解了;
Build(2*root,L,mid);
Build(2*root+1,mid+1,R);
Tree[root]=Tree[root*2]+Tree[root*2+1];
//cout<<Tree[root]<<" ";
}
}
然后线段树的单点更新,应该如何操作呢?肯定是递归下去,因为单点更新肯定会给index所以我按照index和区间[L,R]的包含关系进行递归知道L==R时说明找到了该点,然后就可以修改;但是为什么呢?原因很简单我可以发现线段树有个这样的特点:
就是它的子叶节点对应的区间(可以很明显发现哦,根据上面的图)其实就是元素在原数组的下标(因为这里我简化内存没有写成Tree[root]=a[L],而是直接输入的);这点很重要!!所以根据这个我就可以递归找到端点然后改值,然后递归上去把父节点的值也跟着改动;
所以思路就是这样滴(如果不能理解递归的建议把数据结构的树的知识理解理解就好了,嘻嘻);
本题的AC代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<set>
#include<map>
using namespace std;
const int maxn=5e4+10;
int Tree[maxn<<2],T,n;
int ans[maxn];
void Build(int root,int L,int R){//建树
if(L==R){//递归建二叉树
scanf("%d",&Tree[root]);
//cout<<":"<<Tree[L]<<" ";
return;
}else{
int mid=(L+R)/2;
Build(2*root,L,mid);
Build(2*root+1,mid+1,R);
Tree[root]=Tree[root*2]+Tree[root*2+1];
//cout<<Tree[root]<<" ";
}
}
int Query(int root,int L,int R,int l,int r){//区间和 //其实这个代码写法并不好!只不过比较好理解些
if(l<=L&&R<=r)return Tree[root];//如果对于的区间重合时,那么就表示这个区间刚好对应值
int mid=(L+R)/2;//这里用笔算一下就知道了;
if(r<=mid) return Query(2*root,L,mid,l,r);//如果整个区间都在系统区间的mid的左边
if(l>mid) return Query(2*root+1,mid+1,R,l,r);//如果整个区间都在系统区间的mid的右边
return Query(2*root,L,mid,l,mid)+Query(2*root+1,mid+1,R,mid+1,r);//分两边走
}
void updatapoint(int root,int L,int R,int x,int val){//单点更新
if(L==R){//到了叶结点
Tree[root]+=val;return ;
}
int mid=(L+R)/2;
if(x<=mid)updatapoint(2*root,L,mid,x,val);//这里可以这样理解:如果x在mid的左边,那么我就去左边区间找
else updatapoint((root<<1)|1,mid+1,R,x,val);//如果x在mid的右边,那么我就去右边区间找
Tree[root]=Tree[root<<1]+Tree[(root<<1)|1];//然后求父节点对应的子节点的和,这里的的2*root和2*root+1我用位移运算符+逻辑运算符表示的
}
int main(){
scanf("%d",&T);
char s[10]; //注意这里题上说一个字符,但是我也不知道为什么用char s就wa了,只好改为数组了;估计应该是读文件的时候的问题
int a,b;
for(int i=1;i<=T;i++){
int g=0;
scanf("%d",&n);
Build(1,1,n);//建树
while(scanf("%s",s)){
if(s[0]=='E')break;
scanf("%d %d",&a,&b);
if(s[0]=='A'){
updatapoint(1,1,n,a,b);
}else if(s[0]=='S'){
updatapoint(1,1,n,a,-b);
}else{
ans[g++]=Query(1,1,n,a,b);
}
}
printf("Case %d:\n",i);
for(int j=0;j<g;j++)printf("%d\n",ans[j]);
}
return 0;
}