题目
有一种神奇斑点蛇,蛇如其名,全身都是斑点,斑点数量可以任意改变。
有一天,蒜头君十分的无聊,开始数蛇上的斑点。假设这条蛇的长度是N cm,蒜头君已经数完开始时蛇身的第i上有ai个斑点。
现在蒜头君想知道这条斑点蛇的任意区间的蛇身上一共有多少个斑点。这好像是一个很容易的
事情,但是这条蛇好像是和蒜头君过不去,总是刻意的改变蛇身上的斑点数量。
于是,蒜头君受不了了,加上蒜头君有密集型恐惧症。聪明又能干的你能帮帮他吗?
输入格式
第一行一个正整数 N(N≤50000)表示这条斑点蛇长度为 N 厘米,接下来有 N 个正整数,第 i 个正整数 ai 代表第 i 个斑点蛇第 i 厘米开始时有 ai 个斑点(1≤ai≤50)。
接下来每行有一条命令,命令有 4 种形式:
(1) Add i j,i 和 j 为正整数,表示第 i 厘米增加 j 个斑点(j 不超过 30);
(2) Sub i j,i 和 j 为正整数,表示第 i 厘米减少 j 个斑点(j 不超过 30);
(3) Query i j,i 和 j 为正整数,i≤j,表示询问第 i 到第 j 厘米的斑点总数(包括第 i 厘米和第 j 厘米);
(4) End 表示结束,这条命令在每组数据最后出现;
最多有 40000 条命令。
输出格式
对于每个 Query 询问,输出一个整数并回车,表示询问的段中的总斑点数,这个数保证在int范围内。
分析
核心算法:线段树,查询斑点蛇的第“i”cm到第“j”cm有多少个斑点等同于求区间【i,j】的和。
AC代码
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=50001;
int s[4*MAXN];//依据树状结构原因,最好是开四倍
/*void buildtree(int p,int l,int r){//建一棵树,p为该树一个节点的下标,l,r分别为该下标所代表的区间范围
if(l==r){//将每一个叶子节点赋为0值
s[p]=0;
return;
}
int mid=(l+r)>>1;
buildtree(2*p,l,mid);//建立下标为p的节点的左子树,其左子树下标为p*2,所表示区间范围为l,(l+r)/2
buildtree(2*p+1,mid+1,r);//建立下标为p的节点的右子树,其右子树下标为p*2+1,所表示区间范围为(l+r)/2+1,r
return;
}*/
void modify(int p,int l,int r,int x,int v){//将区间的第x个点的值增加v,所以包括x的区间的值都要增加v,
if(l==r){//找到x点,将其值增加v并返回
s[p]+=v;
return;
}
int mid=(l+r)/2;
if(x<=mid)//x在区间【l,mid】里面,进入下标为p的节点的左子树
modify(p*2,l,mid,x,v);
else//x在区间【mid+1,r】里面,进入下标为p的节点的右子树
modify(p*2+1,mid+1,r,x,v);
s[p]=s[p*2]+s[p*2+1];//将包含x的区间的值都更新
}
int query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y)return s[p];//该节点所代表的范围完全在所求区间范围内
int mid=(l+r)/2,re=0;
if(x<=mid)//下标为p的节点的左子树所代表的范围是否与所求区间有交集
re+=query(p*2,l,mid,x,y);//re代表区间值之和
if(y>mid)//下标为p的节点的右子树所代表的范围是否与所求区间有交集
re+=query(p*2+1,mid+1,r,x,y);
return re;
}
int main()
{
int n,num;
cin>>n;
//buildtree(1,1,n);//建立一颗线段树,跟的下标为1,其范围为【1,n】
for(int i=1;i<=n;i++){
cin>>num;
modify(1,1,n,i,num);//将线段树里面的值更新,
}
string s;
int a,b;
while(cin>>s&&s!="End"){
cin>>a>>b;
if(s=="Add"){
modify(1,1,n,a,b);
}
else if(s=="Sub"){
modify(1,1,n,a,-b);
}
else if(s=="Query"){
cout<<query(1,1,n,a,b)<<endl;
}
}
return 0;
}