(线段树区间更新)-

传送门

问题:

给出一个随意序列,a1, a2, a3, a4, a5 ..... an

1.操作1,给出[ l, r ]  , L = 区间的长度,

    计算 ans = a[ l ] * ( L ) + a[ l+1 ] *(L-1) +....+ a[ r-1 ]*2+a[ r ]*1

2.操作2 ,把序列第 i 个数替换成 num 。

输入:

第一行:n,q  (n个数字,  q组操作)

第二行:n 个 数, 代表序列。

下面 q  行,每行 3 个数,  如果第一个数是  1  ,执行操作   1。。

如果第一个数是  2   ,执行操作   2   。

输出:

对于操作 1 , 输出一个数 ans 。

样例:

样例输入

5 3
1 2 3 4 5
1 1 3
2 5 0
1 4 5

样例输出

10
8

分析:

这道题可以转换成,区间更新,区间求和  的模板题。

对于题中给出的公式,例如序列:1 2 3 4 5。。

那么在区间[2  , 4]进行 操作 1 ,  就是  ans = 2*3 + 3*2 + 4*1 ..

也就是 (2~4) + ( 2~3 )+ (2~2) 。。

那么,本题的重点转换就是!!!!!!!!!!!

用一个 前缀和 数组  b(和算上本位),来建一个线段树

那么对于 [2, 4] ,区间,那么就是  线段树的区间求和! 假设这一步的结果是 t  !!!!!

那么最终的 ans  =   t - b[ l - 1 ]  *  len.!!!!  这里的 len 为 小区间的长度。

比如 ,对于这一个例子,b2  = (1~2).. b3 = (1~3)... b4 = (1~4),  。。

比起需要求的 结果,多了 三个(1~1).  。仔细想想, 重复计算的,就是 b[ l-1 ]~~

重复计算的次数,就是小区间的长度。。

 

对于点更新,在前缀和数组中,就是更新 [i , n]  的值, 看看是增加了还是减少了,~~~

也就是区间更新

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int MAXN  = 1e5+10;
const int MAXN1 = 4*1e5+10;

LL   a[MAXN];
LL   b[MAXN];
LL sum[MAXN1];
LL add[MAXN1];

void pushup(LL root)
{
    sum[root] = sum[root*2] + sum[root*2+1];
}

void build(LL L,LL R,LL rt)
{
    add[rt] = 0;
    if(L == R)
    {
        sum[rt] = b[L];
        return ;
    }
    LL M = (L+R)>>1;
    build(L,M,rt*2);
    build(M+1,R,rt*2+1);
    pushup(rt);
}

void pushdown(LL rt, LL len)
{// 标记下移
    LL Lson = rt<<1;
    LL Rson = rt<<1|1;

    add[Lson] += add[rt];
    add[Rson] += add[rt];
    sum[Lson] += (len - (len>>1)) * add[rt];
    sum[Rson] += (len >> 1 ) * add[rt];
// 本层标记取消
    add[rt] = 0; 
}

LL query(LL rt, LL l,LL r, LL L ,LL R)
{
    if(l == L && r== R)
        return sum[rt];
    if(add[rt])
        pushdown(rt,R-L+1);

    LL ans = 0;
    LL M = (L+R)>>1;
    if(r <= M)
        ans += query(rt*2, l, r, L, M);
    else if(l > M)
        ans += query(rt*2+1,l,r,M+1,R);
    else
    {
        ans += query(rt*2, l, M, L, M);
        ans += query(rt*2+1,M+1,r,M+1,R);
    }
    return ans;
}
/* l~r 区间每个数加num **/
void update(LL rt, LL l, LL r, LL L, LL R, LL num)
{
    if(l <= L && R <= r)
    {
        sum[rt] += (R-L+1)*num;
        add[rt] += num;
        
    }
    else
    {
        LL M = (L + R) >> 1;
        if(add[rt])
            pushdown(rt,r-l+1);
        if(l <= M)
            update(rt<<1, l, r, L, M, num);
        if(r > M)
            update(rt<<1|1,l,r,M+1,R, num);
        pushup(rt);
    }
    return ;
}

int main()
{
    ios::sync_with_stdio(false);
    LL n,q;
    cin >> n >> q;
    for(LL i = 1; i <= n; i++)
        cin >> a[i];
    b[1] = a[1];
    for(LL i = 2; i <= n; i++)
        b[i] = a[i]+b[i-1];

    build(1,n,1);
    while(q--)
    {
        LL t,l,r,t1;
        cin >> t >> l >> r;
        if(l != 1)
            t1 = (r-l+1) * query(1,l-1,l-1,1,n);
        else
            t1 = 0;
        //区间求和
        if( t == 1 )
        {
            LL ans = query(1,l,r,1,n);
            ans -= t1;
            cout << ans << endl;
        }
        //区间更新
        else if( t == 2 )
        {
            LL num = r-a[l];
            a[l] = r;
            r = n;
            update(1,l,r,1,n,num);
        }
    }
   // system("pause");
    return 0;
}

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值