树状数组 差分 区间查询 区间修改 T3 243. 一个简单的整数问题2 - AcWing题库

给定一个长度为 的数列 ,以及 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 都加上
  2. Q l r,表示询问数列中第 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数

第二行 个整数

接下来 行表示 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

,
,

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15

思路

区间查询和区间修改。 区间修改可以用差分技巧来实现, 那么怎么在此基础上实现区间查询呢? 列举一个区间和看看是什么样的: 每个数由若干项 的和组成, 而区间和则是把整个求和。 可以把这些数扩充为一个矩形后, 整个矩形的总和为 。 观察一下多出来的部分, 也可以用一个式子来描述:

故可以通过相减的方式来求出区间和。我们只需要建立两个对于差分数组a的前缀和树状数组 tr1 和 tr2, tr1中维护 的前缀和, tr2中维护 的前缀和。

输出时, 对于区间 , 为 再减去左区间

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL tr1[N], tr2[N];
int a[N];
int n,m;
 
int lowbit(int x)
{
    return x & -x;
}
 
void add(LL tr[], int u, LL x)
{
    for(int i = u; i <= n;i += lowbit(i)) tr[i] += x;
}
 
LL sum(LL tr[], int u)
{
    LL res = 0;
    for(int i = u; i; i -= lowbit(i)) res += tr[i];
    return res;
}
 
LL prefix_sum(int u)
{
    return sum(tr1, u) * (u + 1) - sum(tr2, u);
}
 
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n;i ++) cin >> a[i];
    for(int i = 1; i <= n;i ++)
    {
        LL b = a[i] - a[i - 1];
        add(tr1, i, b);
        add(tr2, i, (LL)b * i);
    }
    
    while(m--)
    {
        char s[2];
        int l,r,d;
        cin >> s >> l >> r;
        if(s[0] == 'Q')
            printf("%lld\n", prefix_sum(r) - prefix_sum(l - 1));
        else {
            cin >> d;
            add(tr1, l, d), add(tr2, l, d * l);
            add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);
        }
    }
    
    return 0;
}