题意
维护一个序列,支持两种操作
- 给一个区间的所有数加 d d d
- 查询一个区间所有数的和
分块
将一个序列分成
n
\sqrt n
n块,每块有
n
\sqrt n
n个元素。用两个数组add、sum分别存储每一块需要加的数以及区间和(加之后)。
所有数加
d
d
d操作,对于整块,
s
u
m
i
+
=
l
e
n
∗
d
,
a
d
d
i
+
=
d
sum_i += len * d, add_i += d
sumi+=len∗d,addi+=d。对于块内的区间,遍历每个数,
w
i
+
=
d
,
s
u
m
g
e
t
(
i
)
+
=
d
w_i += d, sum_{get(i)} += d
wi+=d,sumget(i)+=d。
查询区间和操作,对于整块,
r
e
s
+
=
s
u
m
i
res += sum_i
res+=sumi。对于块内的区间,遍历每个数,
r
e
s
+
=
w
i
+
a
d
d
[
g
e
t
(
i
)
]
res += w_i + add[get(i)]
res+=wi+add[get(i)]。
时间复杂度
m n m\sqrt n mn
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 100010, M = 510;
typedef long long ll;
int n, m;
ll add[M], sum[M];
ll w[N];
int len;
int get(int x)
{
return x / len;
}
void change(int l, int r, ll d)
{
if(get(l) == get(r)){
for(int i=l;i<=r;i++){
w[i] += d;
sum[get(i)] += d;
}
}
else{
int i = l, j = r;
while(get(i) == get(l)) w[i] += d, sum[get(i)] += d, i ++;
while(get(j) == get(r)) w[j] += d, sum[get(j)] += d, j --;
for(int k=get(i);k<=get(j);k++) sum[k] += len * d, add[k] += d;
}
}
ll query(int l, int r)
{
ll res = 0;
if(get(l) == get(r)){
for(int i=l;i<=r;i++){
res += w[i] + add[get(i)];
}
}
else{
int i = l, j = r;
while(get(i) == get(l)) res += w[i] + add[get(i)], i ++;
while(get(j) == get(r)) res += w[j] + add[get(j)], j --;
for(int k=get(i);k<=get(j);k++) res += sum[k];
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
len = sqrt(n);
for(int i=1;i<=n;i++){
scanf("%lld",&w[i]);
sum[get(i)] += w[i];
}
while(m--){
char op[2];
scanf("%s",op);
if(*op == 'C'){
int l, r;
ll d;
scanf("%d%d%lld",&l,&r,&d);
change(l,r,d);
}
else{
int l, r;
scanf("%d%d",&l,&r);
printf("%lld\n",query(l,r));
}
}
return 0;
}