首页 > 代码库 > 士兵杀敌(二)

士兵杀敌(二)

题目来源(数据结构类型:树状数组)

士兵杀敌(二)

时间限制:1000 ms  |  内存限制:65535 KB
难度:5
描述

南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的。

小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧。

南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新增的杀敌数。

输入
只有一组测试数据
第一行是两个整数N,M,其中N表示士兵的个数(1<N<1000000),M表示指令的条数。(1<M<100000)
随后的一行是N个整数,ai表示第i号士兵杀敌数目。(0<=ai<=100)
随后的M行每行是一条指令,这条指令包含了一个字符串和两个整数,首先是一个字符串,如果是字符串QUERY则表示南将军进行了查询操作,后面的两个整数m,n,表示查询的起始与终止士兵编号;如果是字符串ADD则后面跟的两个整数I,A(1<=I<=N,1<=A<=100),表示第I个士兵新增杀敌数为A.
输出
对于每次查询,输出一个整数R表示第m号士兵到第n号士兵的总杀敌数,每组输出占一行
样例输入
5 6
1 2 3 4 5
QUERY 1 3
ADD 1 2
QUERY 1 3
ADD 2 3
QUERY 1 2
QUERY 1 5
样例输出
6
8
8
20

对于很小的n用一般的for循环可以求和或变更内容后求和得出题目的答案,可是对于很大的n来说,程序会因为多次循环而变的很慢(时间就是金钱啊,有木有),所以我们要想法优化数据的存储结构,一个好的数据结构带来的肯定是时间上的减缩,何乐而不为.废话不多说,本题使用树状数组来存储题目数据。首先看图片(来自百度)认识一下树状数组:


我们称数组c为树状数组,它的特点是,c[i]存储的是A[i-2^k + 1]到A[i]的和(和士兵杀敌(一)的数组有的类似,这里的数组c[i]存储的是某一个区间的和),需要说明的是:k为i在二进制形式下有效0的个数(一般用 i & (-i) 直接求出 2^k的值).

那么问题来了.....

1. 如何求出前 i 项的和(求前 i 项和为了的到某一区间的和,这个思想用于士兵杀敌(一)中)。

2. 若数组A[]的某一个元素更新,如何更新数组c[]的值。

对于这两个问题请参考:http://www.java3z.com/cwbwebhome/article/article19/zip/treearray.zip实在不是太懂,汗颜啊!

下面给出代码:

#include<stdio.h>
#define MAX 1000000
int ans[MAX+1];

/*直接计算2^k的值*/
int lowbit(int x)
{
	return x&(-x);
}
/*更新数组位置 i 的值*/
void update(int i,int n,int N)
{
	while(i<=N){
		//printf("i = %d\n",i);
		ans[i]+=n;
		i+=lowbit(i);
	}
}
/*求出前 i 项的和*/
int sum(int n)
{
	int s=0;
	while(n>0){
		s+=ans[n];
		n-=lowbit(n);
	}
	return s;
}
int main()
{
	int N,M;
	int i,t,p,q;
	char ch[6];
	scanf("%d%d",&N,&M);
	for(i=1;i<=N;i++){
		scanf("%d",&t);
		update(i,t,N);
	}
	for(i=1;i<=M;i++){
		scanf("%s %d %d",ch,&p,&q);
		if(ch[0]=='Q')
			printf("%d\n",sum(q)-sum(p-1));
		else
			update(p,q,N);
	}
	return 0;
}


士兵杀敌(二)