洛谷P3372 【模板】线段树 1 【树状数组解法】

题链

这里用树状数组写法;

考虑求[1,x]的区间和 \(sum_x\),于是将原数组差分后,相当于求一个二阶前缀和;

设原数组为 \(a_i\), 差分后的数组为 \(b_i = a_i - a_{i-1}\)

于是 \(a_i = \sum_{j=1}^{i}{b_j}\)

那么[1,x]的区间和为 \(sum_x = \sum_{i=1}^{x}a_i = \sum_{i=1}^{x} \sum_{j=1}^{i}b_j\)

\(sum_x = a_1 + a_2 + \cdots + a_x\)

    \(= (b_1) + (b_1+b_2) + (b_1+b_2+b_3) + \cdots + (b_1+b_2+b_3+\cdots+b_x)\)

    $= xb_1 + (x-1)b_2 + (x-2)b_3 + \cdots + (x-n+1)b_n + \cdots + b_x $

    $= \sum_{n=1}^{x}{(x-n+1)b_n} $

    $= (x+1)\sum_{n=1}^{x}b_n - \sum_{n=1}^{x}b_n \cdot n $

于是分别在树状数组上维护 $\sum_{n=1}^{x}b_n $ 与 $ \sum_{n=1}^{x}b_n \cdot n $

$ ans = sum_r - sum_{l-1} $

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define MS 1000009

int n,m;
LL a[MS];
LL b[MS];
LL p[2][MS];

int lowbit(int x){
    return x&(-x);
}

void add(int rt, int pos, LL val){
    for(;pos<=n;pos+=lowbit(pos)) p[rt][pos] += val;
}

LL query(int pos){
    LL cc = 0;
    for(LL t=pos+1;pos;pos-=lowbit(pos)) cc += t*p[0][pos]-p[1][pos];
    return cc;
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        cin >> a[i];
    }
    for(int i=1;i<=n;i++){
        b[i] = a[i] - a[i-1];
        add(0, i, b[i]);
        add(1, i, b[i]*i);
    }
    while(m--){
        LL op,l,r;
        cin >> op >> l >> r;
        if(op == 1){
            LL x;
            cin >> x;
            add(0, l, x);
            add(0, r+1, -x);
            add(1, l, x*l);
            add(1, r+1, -x*(r+1));
        }
        else if(op == 2){
            LL ans = query(r) - query(l-1);
            cout << ans << "\n";
        }
    }



    return 0;
}


posted @ 2021-08-04 16:18  棉被sunlie  阅读(75)  评论(0)    收藏  举报