POJ 3468 A Simple Problem with Integers (树状数组区间更新)

96 篇文章 0 订阅
44 篇文章 0 订阅

题意:给出N个数,进行Q次操作,有两种操作,一种是区间查询,一种是区间更新.



由于这题感觉没什么附加条件,比较裸,博主懒得写线段树,直接上了树状数组,但是区间更新的时候我是区间内每个点去更新的,所以返回结果果断tle.然后去网上学了一发树状数组区间更新的姿势,比线段树要快,也很巧妙.

用两个bit数组维护前缀和增量 即二维数组bit[2][maxn]

当[l,r]区间增加c时, 不难得出, [1,l-1] 前缀和不变, [l,r]区间内的前缀和增加量为c* (i-l+1), [r+1,N ]区间增量为c*(r-l+1);

建立增加量和i之间的坐标轴,可以看出在l到r区间内点的分布是一条直线,  所以我们可以把斜率用bit[0]数组来维护斜率,  bit[1]数组来维护常量.

维护操作

第一步: 当[l,r]区间内增加c时,即斜率增加c,这时候bit[0]在l这个位置加上c,由于在r+1到n这个区间多加上了c,所以要在r+1这个位置上减去c.

第二步: 第一步对斜率进行了维护,第二步则需要改变常量,所以bit[1]在l这个位置上应当加上c*(-l+1),这样表示在l到N内更新了bit[1]的值

第三步: 最后更新r之后的增量,因为c*(-l+1)在第二部已经更新过了,所以只要bit[1]在r+1位置上加上c*r就行;


AC时间:1954ms  线段树时间在2300到3000.


所以树状数组做一些裸的区间题在代码量和效率上还是比较占优的.


代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n, q;
long long a[101234];
long long bit[2][101234];
long long pesum[101234];
int lowbit(int x)
{
    return x&-x;
}
void add(long long b[], int x,  long long y)
{
    while(x<=n)
    {
        b[x]+=y;
        x+=lowbit(x);
    }
}
long long sea(long long b[],  int x)
{
    long long ret=0;
    while(x>0)
    {
        ret+=b[x];
        x-=lowbit(x);
    }
    return ret;
}
long long queery(int x)
{
    return sea(bit[0], x)*x+sea(bit[1], x);
}
int main()
{
    memset(bit, 0, sizeof(bit));
    memset(pesum, 0, sizeof(pesum));
    scanf("%d%d", &n, &q);
    int i, j;
    for(i=1; i<=n; i++)
    {
        scanf("%lld", &a[i]);

        pesum[i]+=a[i]+pesum[i-1];  //求前缀和
    }

    for(i=1; i<=q; i++)
    {
        char c,d;
        scanf("%c%c", &d, &c);
        if(c=='C')
        {
            int a, b; long long y;
            scanf("%d%d%lld", &a, &b, &y);
            add(bit[0], a, y);      //对应第一步
            add(bit[0], b+1, -y);  //对应第一步
            add(bit[1], a, (1-a)*y); // 对应第二部
            add(bit[1], b+1, y*b);   //对应第三步
        }
        if(c=='Q')
        {
            int a, b;
            scanf("%d%d", &a, &b);

            printf("%lld\n", queery(b)-queery(a-1)-pesum[a-1]+pesum[b]);  //区间值加上区间增量
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值