【分块】 A simple problem with Integer2
传送门
题意
给定一个长度为\(N\)的数列\(A\),以及\(M\)个指令,指令包含两种
-
\((C,l,r,d)\),把\(A[l],A[l+1],\dots ,A[r]\)都加上\(d\)
-
\((Q,l,r)\),询问区间\(l\sim r\)的区间和
数据范围
\(\begin{array}{l}1 \leq N, M \leq 10^{5} \\ |d| \leq 10000 \\ |A[i]| \leq 1000000000\end{array}\)
题解
分块做法,将长度为\(n\)的序列分作\(\sqrt{n}\)块,
-
维护两个属性,当前完整块的和\(sum\),当前整个块共同加的数\(add\)
-
查询和修改时,如果左右界都位于一个块直接进行暴力,
-
否则先将左右的块内暴力处理,然后处理中间可能存在的完整块
每次修改和查询最多有\(2\)块是暴力做的,且暴力的长度\(\leq \sqrt{n}\),整个段的长度\(\leq \sqrt{n}\)
所以单次查询和修改的复杂度为\(O(\sqrt{n})\),总的时间复杂度为\(O((N + M)\times \sqrt{n})\)
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define ll long long
const int N=1e5+10,M=350;
int a[N];
ll sum[M],add[M];
int len;
int n,m;
int get(int id){
return id/len;
}
void modify(int l,int r,int d){
if(get(l) == get(r)) rep(i,l,r) a[i]+=d,sum[get(i)]+=d;
else{
int i=l,j=r;
while( get(i) == get(l) ) a[i]+=d,sum[get(i)]+=d,i++;
while( get(j) == get(r) ) a[j]+=d,sum[get(j)]+=d,j--;
rep(k,get(i),get(j)) sum[k]+=len*d,add[k]+=d;
}
}
ll query(int l,int r){
ll res=0;
if(get(l)==get(r)){
rep(i,l,r) res += a[i] + add[get(i)];
}
else{
int i=l,j=r;
while(get(i) == get(l)) res+=a[i]+add[get(i)],i++;
while(get(j) == get(r)) res+=a[j]+add[get(j)],j--;
rep(k,get(i),get(j)) res+=sum[k];
}
return res;
}
int main(){
cin>>n>>m;
len = sqrt(n);
rep(i,1,n) {
cin>>a[i];
sum[get(i)] += a[i];
}
char op[2];
int l,r,d;
while(m--){
cin>>op>>l>>r;
if(op[0] == 'C'){
cin >> d;
modify(l,r,d);
}
else
cout<<query(l,r)<<endl;
}
}