树状数组进阶
众所周知,线段树是可以进行区间修改的,那么与其类似的数据结构——树状数组是否也可以完成这个操作呢?
有没有办法让区间修改变为单点修改呢?
差分
引入差分数组
b
i
=
a
i
−
a
i
−
1
b_i = a_i - a_{i-1}
bi=ai−ai−1
则有以下公式:
a
i
=
∑
j
=
1
i
b
j
a_i = \sum_{j=1}^i b_j
ai=j=1∑ibj
s
i
=
∑
j
=
1
i
a
i
=
∑
j
=
1
i
∑
k
=
1
j
b
k
s_i = \sum_{j=1}^i a_i=\sum_{j=1}^i \sum_{k=1}^j b_k
si=j=1∑iai=j=1∑ik=1∑jbk
交换
∑
\sum
∑顺序,
s
i
=
∑
k
=
1
i
∑
j
=
k
i
b
k
=
∑
k
=
1
i
(
i
−
(
k
−
1
)
)
×
b
k
=
i
×
∑
k
=
1
i
b
k
−
∑
k
=
1
i
(
k
−
1
)
×
b
k
s_i = \sum_{k=1}^i \sum_{j=k}^i b_k=\sum_{k=1}^i (i-(k-1)) \times b_k=i \times \sum_{k=1}^i b_k-\sum_{k=1}^i (k-1) \times b_k
si=k=1∑ij=k∑ibk=k=1∑i(i−(k−1))×bk=i×k=1∑ibk−k=1∑i(k−1)×bk
可见,现在只需要维护两个前缀和就可以完成了
题目
code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define G getchar
#define nxt(x) (x & (- x))
using namespace std;
int read()
{
char ch;
for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
int n = 0 , w;
if (ch == '-')
{
w = -1;
ch = G();
} else w = 1;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n * w;
}
const int N = 500005;
int a[N] , b[N] , n , m , v , op , l , r;
long long s1[N] , s2[N];
void ins (int x , int v)
{
long long vv = (long long) (x - 1) * v;
for (;x <= n; x = x + nxt(x))
{
s1[x] = s1[x] + v;
s2[x] = s2[x] + vv;
}
}
long long sum (int x)
{
long long s = 0;
int X = x;
for (; x ; x = x - nxt(x))
s = s + s1[x] * X - s2[x];
return s;
}
int main()
{
freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n = read();
m = read();
for (int i = 1; i <= n ; ++i)
{
a[i] = read();
b[i] = a[i] - a[i-1];
ins(i , b[i]);
}
for (int i = 0; i < m; ++i)
{
op = read();
if (op == 1)
{
l = read();
r = read();
v = read();
ins(l , v);
ins(r + 1 , -v);
}
else
{
l = read();
printf("%lld\n", sum(l) - sum(l-1));
}
}
}