分块
A Simple Problem with Integers
给出了一个序列,你需要处理如下两种询问。"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。"Q a b" 询问[a, b]区间中所有值的和。
Input:
第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.
第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。
接下来Q行询问,格式如题目描述。
Output
对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。
思路:
由于是对区间进行操作,若每次操作都修改每个元素的值则必定超时.故我们可以将这个数组分成几块,每次对完整的块就对整个块进行操作,两边残缺的块一个个操作即可.更新时相似,对于完整的块直接计入答案,再一个个加上两边残缺的块.每次只用维护每个块的和即可(部分注意事项见代码).
AC代码:
#include<cstdio>
#include<cmath>
char s[4];
int A[100005];
int S,ADD[320];
long long sum[320];
long long Read() {//读入优化对于POJ的题目作用挺大的
char c;
int flag=1;
long long res=0ll;
while(c=getchar(),c<48) {
if(c=='-')flag=-1;
}
do res=1ll*(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>=48);
res*=flag;
return res;
}
void add(int l,int r,int d) {
int ka=l/S,kb=r/S;//ka,kb为块编号.
if(ka==kb){
for(int i=l;i<=r;i++)A[i]+=d;
sum[ka]+=1ll*(r-l+1)*d;
return;
}
for(int i=l; i<(ka+1)*S; i++)A[i]+=d,sum[ka]+=1ll*d;//前面残缺的块
for(int i=kb*S; i<=r; i++)A[i]+=d,sum[kb]+=1ll*d;//后面残缺的块
for(int i=ka+1; i<kb; i++)ADD[i]+=d,sum[i]+=1ll*d*S;//中间完整的块
//此处的ADD为了之后查询到一个块中间的一部分的值
}
long long find(int l,int r) {
int ka=l/S,kb=r/S;
long long ans=0ll;
if(ka==kb) {
for(int i=l; i<=r; i++)ans+=1ll*A[i]+ADD[ka];//加上这个块的增值
return ans;
}
for(int i=l; i<(ka+1)*S; i++)ans+=1ll*A[i]+ADD[ka];//前面残缺的块
for(int i=kb*S; i<=r; i++)ans+=1ll*A[i]+ADD[kb];//后面残缺的块
for(int i=ka+1; i<kb; i++)ans+=1ll*sum[i];//中间完整的块
return ans;
}
int main() {
int n,q;
n=Read();
q=Read();
S=sqrt(n);
for(int i=1; i<=n; i++)A[i]=Read(),sum[i/S]+=1ll*A[i];//初始化块的和
while(q--) {
scanf("%s",s);
if(s[0]=='C') {
int a,b,d;
a=Read();
b=Read();
d=Read();
add(a,b,d);
} else {
int l,r;
l=Read();
r=Read();
printf("%lld\n",find(l,r));
}
}
}
注意:前面第一个块一定是不完整的.