1 10 1 2 3 4 5 6 7 8 9 10 Query 1 3 Add 3 6 Query 2 7 Sub 10 2 Add 6 3 Query 3 10 End
Case 1: 6 33 59
简单的线段树入门题目,只包含建立并初始化树,更新节点信息,向上更新以及查询四个操作,这里采用数组来模拟树,因为线段树可以理解为一颗二叉树,所以对于一个节点i,它的左子树可以设置为i*2,右子树可以设置为i*2+1,这样既不会冲突,也不会造成空间浪费,还可以拜托以往用指针建树时的繁琐以及各种错误,因为乘2用移位来表示更快,即i*2=i<<1,i*2+1=i<<1|1,从而更加节约时间,对于每个节点,不存储它的区间,区间可以在计算的过程中计算出来。
#include <iostream> #include <stdio.h> #define MAX 50010//设置线段树的叶子节点数 using namespace std; int tree[MAX<<2];//总结点数必须设置为叶子节点数的四倍以上 void PushUp(int rt)//更新根节点rt的信息 { tree[rt]=tree[rt<<1]+tree[rt<<1|1]; } void Update(int p,int add,int l,int r,int rt)//将位置p的值更新add,当前的左右区间为l,r,根节点为rt { if(l==r) { tree[rt]+=add; return; } int m=(l+r)>>1; if(p<=m) Update(p,add,l,m,rt<<1); else Update(p,add,m+1,r,rt<<1|1); PushUp(rt); } void Creat(int l,int r,int rt) { if(l==r) { scanf("%d",&tree[rt]); return; } int m=(l+r)>>1; Creat(l,m,rt<<1); Creat(m+1,r,rt<<1|1); PushUp(rt); } int Query(int L,int R,int l,int r,int rt) { if(L<=l&&R>=r) { return tree[rt]; } int m=(l+r)>>1; int sum=0; if(L<=m) sum+=Query(L,R,l,m,rt<<1); if(R>m) sum+=Query(L,R,m+1,r,rt<<1|1); return sum; } int main() { int t,n,cas,i,j,o1,o2; string op; cin>>t; for(cas=1;cas<=t;cas++) { scanf("%d",&n); Creat(1,n,1); cout<<"Case "<<cas<<":"<<endl; while(cin>>op) { if(op[0]=='A') { scanf("%d%d",&o1,&o2); Update(o1,o2,1,n,1); } else if(op[0]=='S') { scanf("%d%d",&o1,&o2); Update(o1,-o2,1,n,1); } else if(op[0]=='Q') { scanf("%d%d",&o1,&o2); cout<<Query(o1,o2,1,n,1)<<endl; } else { break; } } } return 0; }