中文题目,题意还是很简单的,给你n个数,然后有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个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
要求对每个Query输出相应答案。
很明显,对于区间的操作,当然是用线段树来解比较方便了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define N 50010
#define INF 999999999
using namespace std;
struct tree
{
int l,r,sum;
};
tree p[N*4];
int a[N];
void build(int i,int l,int r)//建树
{
p[i].l=l;
p[i].r=r;
int mid=(l+r)/2;
if(l==r)
p[i].sum=a[l];//到叶子结点时,就把该点对应的数组a的元素储存到sum里;
if(l!=r)
{
build(i*2+1,l,mid);//递归建立左子树;
build(i*2+2,mid+1,r);//递归建立右子树;
p[i].sum=p[i*2+1].sum+p[i*2+2].sum;//第i个区间的sum值等于它的左儿子和右儿子的sum值之和;
}
}
void Add(int i,int k,int m)//对该点进行增加或减少操作;
{
if(p[i].l==p[i].r)//找到该点,将该点的sum值进行加或减m的操作;
{
p[i].sum+=m;
return;//这里的返回很重要不能省
}
int mid=(p[i].l+p[i].r)/2;
if(k<=mid)
Add(i*2+1,k,m);
else
Add(i*2+2,k,m);//采用递归的方法找要更改的点
p[i].sum=p[i*2+1].sum+p[i*2+2].sum;//在递归返回的过程中顺便将各个区间的sum值相应的改变
}
int query(int i,int s,int e)//查询操作,分在区间左侧,在区间右侧,包含在区间内三种情况;
{
if(s<=p[i].l&&e>=p[i].r)
return p[i].sum;
int mid=(p[i].l+p[i].r)/2;
if(e<=mid)
return query(i*2+1,s,e);
if(s>mid)
return query(i*2+2,s,e);
return query(i*2+1,s,e)+query(i*2+2,s,e);
}
int main()
{
int t,n,i,j;
int k=0;
char s[10];
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
build(0,1,n);
printf("Case %d:\n",++k);
while(1)
{
scanf("%s",s);
if(s[0]=='E')
break;
scanf("%d%d",&i,&j);
if(s[0]=='Q')
printf("%d\n",query(0,i,j));
else if(s[0]=='A')
Add(0,i,j);
else if(s[0]=='S')
Add(0,i,-j);//减一个数相等于加这个数的相反数;
}
}
return 0;
}