题目大意:
题目大意:
题目链接:http://poj.org/problem?id=3468
给出一个初始数列,有两种操作:
- Q x y Q\ x\ y Q x y,输出这个数列 x x x到 y y y之间的数字和。
- C x y z C\ x\ y\ z C x y z,将这个数列的 x x x到 y y y之间的数加上 z z z。
思路:
此题线段树也可做:https://blog.csdn.net/SSL_ZYC/article/details/81907648
树状数组本来只支持单点修改和单点查询。可是如果加上前缀和就有了区间修改的能力。那么应该怎样完成区间查询呢?
设
s
u
m
sum
sum数组为前缀和,那么由于
∑
i
=
1
x
s
u
m
[
i
]
=
a
[
x
]
\sum^{x}_{i=1}sum[i]=a[x]
∑i=1xsum[i]=a[x],所以就有
∑
i
=
1
x
a
[
i
]
=
∑
i
=
1
x
∑
j
=
1
i
b
[
j
]
\sum^{x}_{i=1}a[i]=\sum^{x}_{i=1} \sum^{i}_{j=1}b[j]
∑i=1xa[i]=∑i=1x∑j=1ib[j]。
然后(摘自《算法竞赛进阶指南》):
∑
i
=
1
x
∑
j
=
1
i
b
[
j
]
\sum^{x}_{i=1} \sum^{i}_{j=1}b[j]
i=1∑xj=1∑ib[j]
=
∑
i
=
1
x
(
x
−
i
+
1
)
×
b
[
i
]
=\sum^{x}_{i=1}(x-i+1)\times b[i]
=i=1∑x(x−i+1)×b[i]
=
(
x
+
1
)
∑
i
=
1
x
b
[
i
]
−
∑
i
=
1
x
i
×
b
[
i
]
=(x+1)\sum^{x}_{i=1}b[i]-\sum^{x}_{i=1}i\times b[i]
=(x+1)i=1∑xb[i]−i=1∑xi×b[i]
那么就维护两个树状数组,一个储存普通的
c
[
i
]
c[i]
c[i],另一个储存
x
×
z
x\times z
x×z即可。
代码:
#include <cstdio>
#include <iostream>
#define ll long long
using namespace std;
int n,m,x,y;
ll sum[100001],c[2][100001],a[100001],z,ans;
char ch;
ll ask(int id,int x)
{
ll _ans=0;
for (;x;x-=x&-x) _ans+=c[id][x];
return _ans;
}
void add(int id,int x,ll y)
{
for (;x<=n;x+=x&-x) c[id][x]+=y;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
while (m--)
{
cin>>ch;
if (ch=='Q')
{
scanf("%d%d",&x,&y);
ans=sum[y]+(y+1)*ask(0,y)-ask(1,y);
ans-=sum[x-1]+x*ask(0,x-1)-ask(1,x-1);
cout<<ans<<endl;
}
else
{
scanf("%d%d",&x,&y);
scanf("%lld",&z);
add(0,x,z);
add(0,y+1,-z);
add(1,x,(ll)x*z);
add(1,y+1,-(ll)(y+1)*z);
}
}
return 0;
}