HDU-1166 敌兵布阵(树状数组和线段树)

7 篇文章 0 订阅
3 篇文章 0 订阅

这个题目就是对一组数据的更新和分段求和。由于数据过大,我们就会想到用树状数组。当然,先要了解它。这个题就是它的一个典型应用

树状数组(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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值