一道luogu没有的题
Description
给定一个数列(N(N≤10^5)),给出几次操作
-
( ext{C l r d}),把数列中第(l)~(r)个数都加(d)
-
( ext{Q l r}),询问数列中第(l)~(r)个数的和
Solve
显然这道题线段树秒切,但是手贱去写了树状数组。
用一种差分的思想,用(b)数组的前缀和(sum_{i=1}^xb[i])表示(a[x])增加的值。那么序列(a)的前缀和整体增加的值就是(sum_{i=1}^xsum_{j=1}^ib[j])
上式可以改写成:
[sum_{i=1}^xsum_{j=1}^ib[j]=sum_{i=1}^x(x-i+1) ast b[i]=(x+1)sum_{i=1}^x b[i]- sum_{i=1}^x i ast b[i]
]
于是我们就可以用两个树状数组,另一个维护(i ast b[i])的前缀和,和上面的方法类似。
在加操作时,我们需要
1.在树状数组(c_0)中,把位置(l)上的数加(d)
2.在树状数组(c_0)中,把位置(r+1)上的数减(d)
3.在树状数组(c_1)中,把位置(l)上的数加(last d)
4.在树状数组(c_1)中,把位置(r+1)上的数加((r+1) ast d)
统计答案就是把答案拆成(1)$r$和$1$(l-1)来处理
答案为((sum[r]+(r+1)ast ask(c_0,r)-ask(c_1,r))-(sum[l-1]+last ask(c_0,l-1)-ask(c_1,l-1)))
这种思想可以推广一下
Code
#include <bits/stdc++.h>
#define ll long long
#define MAXN 100100
using namespace std;
int n, q;
ll A[MAXN], B[2][MAXN];// B[0]为修改的累加, B[1]为i*修改的累加
void add(int k, int x, ll y)
{
for (; x <= n; x += x&-x)
{
B[k][x] += y;
}
}
ll ask(int k, int x)
{
ll ans = 0;
for (; x; x -= x&-x)
{
ans += B[k][x];
}
return ans;
}
int main()
{
scanf("%d%d", &n, &q);
int a;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a);
A[i] = A[i-1]+a;
}
getchar();
char tmp;
int r, l, d;
for (int i = 0; i < q; i++)
{
scanf("%c", &tmp);
if (tmp == 'C')
{
scanf("%d%d%d", &l, &r, &d);
add(0, l, d);
add(0, r+1, -d);
add(1, l, (ll)l*d);
add(1, r+1, (ll)-(r+1)*d);
}
else if (tmp == 'Q')
{
scanf("%d%d", &l, &r);
printf("%lld
", (ll)(A[r]+(r+1)*ask(0, r)-ask(1, r))
- (A[l-1]+l*ask(0,l-1)-ask(1, l-1)));
}
getchar();
}
return 0;
}