这个题目就是对一组数据的更新和分段求和。由于数据过大,我们就会想到用树状数组。当然,先要了解它。这个题就是它的一个典型应用
树状数组(256ms) 操作: 区间求和,单点更新.
#include<stdio.h>
#include<string.h>
int a[50005],c[50005],n;;
void Add(int x,int s)
{
while(x<=n)
{
c[x]+=s;
x+=(x&-x);
}
}
int sum(int x)
{ int s=0;
while(x>0)
{ s+=c[x];
x-=(x&-x);
}
return s;
}
main()
{
char z[10];
int T,p=1,k,m;
scanf("%d",&T);
while(T--)
{
memset(c,0,sizeof(c));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Add(i,a[i]);
}
printf("Case %d:\n",p++);
while(scanf("%s",z),z[0]!='E')
{
if(z[0]=='A'){
scanf("%d%d",&k,&m);
Add(k,m);
}
if(z[0]=='S'){
scanf("%d%d",&k,&m);
Add(k,-m);
}
if(z[0]=='Q'){
scanf("%d%d",&k,&m);
printf("%d\n",sum(m)-sum(k-1));
}
}
}
}
上面是以前写的代码丑陋不堪。
最近学习线段树,也从这个简单的开始吧。不过自己先还不会写,参考大牛的。略懂了线段树。点击打开链接
关键是三个操作:建,查,改。
代码如下(346ms)
#include<stdio.h>
#define M 55555
int sum[M*4];
void pushup(int d){
sum[d]=sum[d<<1]+sum[d<<1|1];
}
void build(int l,int r,int d) //构造线段树
{
if(l==r){
scanf("%d",&sum[d]);
return;
}
int mid=(l+r)>>1;
build(l,mid,d<<1);
build(mid+1,r,d<<1|1);
pushup(d);
}
void update(int pos,int add,int l,int r,int d){
if(l==r){
sum[d]+=add;
return ;
}
int m=(l+r)>>1;
if(pos<=m) update(pos,add,l,m,d<<1); //如果小于m,更新左孩子
else update(pos,add,m+1,r,d<<1|1); //同理
pushup(d);
}
int query(int L,int R,int l,int r,int d){
if(L<=l&&r<=R){ //访问区间有包含关系,直接就输出
return sum[d];
}
int m=(l+r)>>1;
int res=0;
if(L<=m) res+=query(L,R,l,m,d<<1); //如果小于m,访问左孩子
if(R>m) res+=query(L,R,m+1,r,d<<1|1); //同理
return res;
}
int main(){
int t,n,a,b;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
printf("Case %d:\n",cas);
scanf("%d",&n);
build(1,n,1);
char op[10];
while(scanf("%s",op)){
if(op[0]=='E') break;
scanf("%d %d",&a,&b);
if(op[0]=='Q') printf("%d\n",query(a,b,1,n,1));
else if(op[0]=='S')update(a,-b,1,n,1);
else update(a,b,1,n,1);
}
}
return 0;
}