繁忙的公路|算法题目

题目:繁忙的公路 时间限制: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;
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值