题意:
本题要求运用树状数组实现 区间修改 和 区间查询。
思路:
在 上一题 中,我们用树状数组维护了一个原数组a的差分数组b,对于每一条指令“C l r d
”,把b[i]
加上d
, 再把b[r+1]
减去d
。
已经讨论得知的是:b
的1~x
前缀和b[1~x]
就是经过这些指令后,a[x]
增加的值。
那么a
序列的前缀和a[1~x]
整体增加的值就是
上式可以经过推导进一步改写为:
以上公式即用于求解区间[1, x]
的所有元素和
推导过程:
因此在这道题中我们可以考虑增加一个树状数组,用于维护i*b[i]
的前缀和 ,上面的式子就可以直接进行计算了。
具体地说,我们建立两个树状数组,分别为c1
, c2
,起初全赋值为0
,对于每一条指令“C l r d
”,执行四个操作:
- ①
c1
的l
位置加上d
。 - ②
c1
的r+1
位置减去d
。 - ③
c2
的l
位置加上l*d
。 - ④
c2
的r+1
位置减去(r+1)*d
。
对于每一条指令“Q l r
”,我们直接套取上面推出来的式子进行代入计算即可。
这道题带给我们一种思想,分离包含有多个变量的项,使公式中不同变量之间互相独立,这种思想很重要。
时间复杂度:
O(mlogn)
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
int n, m;
int a[N], c1[N], c2[N];
int read() {//快读
int x=0,f=1; char c=getchar();
while(c<'0' || c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
}
inline int lowbit(int x) {return x & (-x);}
int ask(int c[], int x)
{
int ans = 0;
while(x) {ans+=c[x], x-=lowbit(x);}
return ans;
}
int add(int x, int y, int c[])
{
for(; x<=n; x+=lowbit(x)) c[x]+=y;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
{
a[i] = read();
add(i, a[i]-a[i-1], c1), add(i, i*(a[i]-a[i-1]), c2);
}
while(m--)
{
char op[2];
int l, r, d;
scanf("%s", op);
l = read(), r = read();
if(*op=='C')
{
d = read();
add(l, d, c1), add(l, l*d, c2);
add(r+1, -d, c1), add(r+1, -(r+1)*d, c2);
}
else
{
printf("%lld\n", (r+1)*(ask(c1, r)) - ask(c2, r) - l*(ask(c1, l-1)) + ask(c2, l-1));
}
}
return 0;
}