直接从公式开始:
$$S_n = (1+n) \times \sum_{i=1}^n{d_i} + \sum_{i=1}^n{i \times d_i} = (n+1)a_n + \sum_{i=1}^n{i \times d_i}$$
$a_i$是原数组,
$tr1$保存的是$a_i$的差分数组$d_i$,
而$tr2$是$\sum_{i=1}^n{i \times d_i}$这整坨东西的差分, 他跟$tr1$的关系完全是根据$d_i$来构成映射关系
由于$tr2$完全就是公式给出, 那么显然$tr2$差分数组的第$i$项就是$i\times d_i$
其中每次sum(x)获取前缀和得到的结果就是$\sum_{i=1}^x{i \times d_i}$
每次我们要修改$[l, r]$区间上的$a_i$(集体加上$k$), 我们就会令$tr1$上的$d[l]+=k$, $d[r+1]-=k$;
改完$tr1$上的$d_i$然后才经过$d_i$把改变传递给$tr2$, 修改$tr2$上对应的$i$位置上的$i\times d_i$, 也就是$tr2$上第l个项
从$l\times d_l$变成 $l \times (d_l + k)$, 也就是第$l$项加上$k$, 同理跟着把第$r+1$项从$(r+1)\times d_{r+1}$变成
$(r+1)\times (d_{r+1} - k)$, 也就是第$r+1$项 减去$(r+1) \times k$
重点是这2句话:
$tr2$跟$tr1$的关系完全是根据$d_i$来构成映射关系
“然后才经过$d_i$把改变传递给$tr2$”
参考代码
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 100010;
typedef long long LL;
int a[N];
LL tr1[N], tr2[N];
int n, m;
void add(LL tr[], int x, LL c)
{
for (; x <= n; x += x & -x) tr[x] += c;
}
LL sum(LL tr[], int x)
{
LL res = 0;
for (; x; x -= x & -x) res += tr[x];
return res;
}
LL get_seg(int x)
{
return sum(tr1, x) * (x + 1) - sum(tr2, x);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
add(tr1, i, a[i] - a[i-1]);
add(tr2, i, (LL)i * (a[i] - a[i-1]));
}
while (m -- )
{
int l, r, d;
char op[2];
scanf("%s", op);
if (op[0] == 'C')
{
scanf("%d%d%d", &l, &r, &d);
add(tr1, l, d);
add(tr1, r+1, -d);
add(tr2, l, d * l);
add(tr2, r+1, -d * (r+1));
}
else
{
scanf("%d%d", &l, &r);
printf("%lld\n", get_seg(r) - get_seg(l - 1));
}
}
return 0;
}