【题目描述】
有nnn个营地,已知每个营地的人数,有四条命令:
(1)Add(1) Add(1)Add iii jjj,iii和jjj为正整数,表示第iii个营地增加jjj个人(jjj不超过303030)
(2)Sub(2)Sub(2)Sub iii jjj ,iii和jjj为正整数,表示第iii个营地减少jjj个人(jjj不超过303030);
(3)Query(3)Query(3)Query iii jjj ,iii和jjj为正整数,i≤ji\leq ji≤j,表示询问第iii到第jjj个营地的总人数;
(4)End(4)End(4)End 表示结束,这条命令在每组数据最后出现;
【输入格式】
第一行一个整数TTT,表示有TTT组数据。
每组数据第一行一个正整数N(N≤50000N \leq 50000N≤50000),表示敌人有N个工兵营地,接下来有NNN个正整数,第iii个正整数aia_iai代表第iii个工兵营地里开始时有aia_iai个人(1≤ai≤501 \leq a_i \leq 501≤ai≤50)。
接下来每行有一条命令,最多400004000040000条。
【输出】
对第i组数据,首先输出“CaseCaseCase i:i:i:”和回车,
对于每个QueryQueryQuery询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在intintint以内。
【样例输入】
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
算法分析
线段树模板题目。
单点修改,区间查询。
#include
#define N 50010
using namespace std;
int T,n,a[N],s[4*N]; //4倍n
void built(int k,int l,int r) //建线段树
{
if(l==r) {s[k]=a[l];return;}
int mid=(l+r)/2;
built(2*k,l,mid);
built(2*k+1,mid+1,r);
s[k]=s[k*2]+s[k*2+1];
}
void update(int k,int l,int r,int x,int v) //单点更新
{
if(l==r&&l==x){s[k]+=v;return;} //找到单点修改
if(xr) return;
int mid=(l+r)/2;
if(l<=x&&x<=mid) update(k*2,l,mid,x,v);
if(mid+1<=x&&x<=r) update(k*2+1,mid+1,r,x,v);
s[k]=s[k*2]+s[k*2+1]; //回溯,维护区间和
}
int ask(int k,int l,int r,int x,int y) //区间查询
{
if(yr) return 0; //不相交
if(x<=l&&r<=y) return s[k]; //包含
int mid=(l+r)/2;
return ask(k*2,l,mid,x,y)+ask(k*2+1,mid+1,r,x,y);
}
int main(){
scanf("%d",&T);
int count=1;
while(count<=T)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
built(1,1,n);
char s[10];
int x,y;
printf("Case %d:\n",count);
while(1)
{
scanf("%s",s);
if(s[0]=='E') break;
scanf("%d%d",&x,&y);
if(s[0]=='A') update(1,1,n,x,y);
if(s[0]=='S') update(1,1,n,x,-y);
if(s[0]=='Q') printf("%d\n",ask(1,1,n,x,y));
}
count++;
}
return 0;
}