题目:繁忙的公路 时间限制:6000MS 内存限制:65535K 提交次数:0 通过次数:0
题型: 编程题 语言: G++;GCC Description
在一条笔直的大道(单方向行车道)上,汽车川流不息。道路从起点到终点,等距离的标记了1到N,
即起点是1,然后分别是2、3、4…,终点是N。每一个标记处,安装了智能探头,可以感知 在该点处车辆的增减数量。
一开始,整条道路上,没有车,然后,是不断接收到的智能探头发回的信息,格式如下: H 5 9
H表明收到的是智能探头的回传信息,5表示标记5处的车辆信息,9表示该处车辆增加了9辆。
同时,在某个时刻,需要查询在一段连续的道路上,共有多少辆车 查询格式如下: Q 3 10
Q表明收到的是查询,3是起点,10是终点(包括3和10两处) 要求编程实现正确处理上述信息处理和查询输入格式 第一行一个整数N(1<=N<=1,000,000),表示标记范围是1到N
第二行一个整数M(1<=M<=100,000),表示探头信息(或查询)的总数量 此后M行,每行一个探头信息或查询请求输出格式 每逢遇到查询的时候,输出查询范围内的有多少辆车,占一行,查询结果最大不超过2的63次方
输入样例 10 4 H 5 10 Q 1 10 H 6 20 Q 1 10
输出样例 10 30
提示 开始时,整条路上没有车辆
因为题目输入输出的范围过大,如果用普通的数组进行存储和运算会引起超时错误,所以解这道题,需要用到一种高级数据结构–树状数组,树状数组数据的存储规则比较特殊,巧妙地解决了运算量过大超时的问题,接下来简单介绍一下树状数组和详述这道题的解题过程
这道题目有一个要求是要不定时地查询某一区间的车辆数,可能大家最先想到的会是用一个数组把每一个点的车辆数记下来,查询的时候遍历加一下数组就完成了,这个方法虽然是可行的,但是当要查询的区间过大时就会出现超时。这是可能大家就会想,那我们直接保存从1开始到某一点这个区间里面的车辆总数,查询某一区间就直接终点减起点就可以算出查询区间的车辆数了,不就解决了查询超时的问题了吗。的确是解决了查询的运算时间问题,但是却会引起更新操作的超时,因为每次一个区间的变化都会引起后面所有区间的变化,虽然是提前算了总和,但却在算总和的操作上浪费了太多时间。这个时候就要用到计算机里面常用的中庸思想了,就是在一个数组里面一部分保存的是单点的车辆数,一部分保存的是区间的车辆数,这就是树状数组的特点了
树状数组
一种特殊的数组,其物理结构是一个数组,但其逻辑结果却是一棵树,以这道题为例,它的奇数位保存的是单点的车辆数,偶数位保存的是从某一点到该点这一区间的车辆总数,如下图所示
这样的结构就便于我们计算和更新了:在查询时,找寻所要查询区间所包含的区间和单点,可快速计算出查询区间的车辆总数。在更新时,由于并不是保存所有的和,所以只需更新修改点及其之后记录总和的元素,时间就比之前的存所有车辆数总和的方法减少一半。具体如何实现简单又快速的对某一区间的求和呢?
假设数组a是我们增删改查的对象,但树状数组的思想维护的是 b 数组, 从上面的图我们可以看到,b[i]不是通常意义上的1~i 元素的和;
b数组的规则,其中b8 = b4+b6+b7+a8,b6 = b5+a6……可以看到,b8可以看作a1~a8的左半边和+右半边和,而其中左半边和是确定的b4,右半边其实也是同样的规则把a5~a8一分为二……继续下去都是一分为二直到不能分,其实树状数组就是巧妙的利用了二分,使得查询操作复杂度变为O(1)
树状数组的实现
在树状数组中,为了更加方便而快速地查询,我们定义了函数lowbit,lowbit这个函数的功能就是求某一个数的二进制表示中最低的一位1,举个例子,x = 6,它的二进制为110,那么lowbit(x)就返回2。这表明在更新b[6]时下一个要更新b[6+2],在查询时它的前一个要加上的元素是b[6-2]
具体代码实现
#include <iostream>
using namespace std;
int N,d[1000010]={0},s[1000010]={0};
int m;
int lowbit(int x)///求某个点的管辖范围
{
return x&(-x);
}
void update(int x,int num)///修改树形数组
{
while(x<=N)
{
d[x]+=num;
x+=lowbit(x);
}
}
long long getsum(int x)///区间求和即:s[1]+s[2]+....+s[x]
{
long long s=0;
while(x>0)
{
s+=d[x];
x-=lowbit(x);
}
return s;
}
int main()
{
int a,b;
cin>>N>>m;
string logo;
while(m--)
{
cin>>logo>>a>>b;
if(logo=="H")
{
update(a,b);
}
else
{
cout <<getsum(b)-getsum(a-1)<< endl;
}
}
return 0;
}