分块方法:
把 (n) 个元素每 (S) 个一块,分成 (lceil frac{n}{S} ceil) 个块,维护每个块的加法标记和总和。
对于查询操作,直接把整块的加上;非整块的暴力加。
时间复杂度:(O(2 imes S+lceil frac{n}{S} ceil))
对于区间加操作,整块的加标记和总和;非整块的,暴力加。
时间复杂度:(O(2 imes S+lceil frac{n}{S} ceil))
为让时间复杂度平衡,令(S=sqrt{n}).
可得,时间复杂度均摊为 (n imes sqrt{n})
虽然效率不如之前写的线段树高,但是码量少,实现快。基于暴力的一种算法。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=LONG_LONG_MAX;
const int N=1e6+1;
#define EPS 1e-8
inline ll read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar();return x*f;}
int n,m,S;
ll a[N],sum[N],add[N];
inline int kuai(int x) {
return x/S+1; //返回i属于的块的位置
}
inline void update(int l,int r,int x) {
int L=kuai(l),R=kuai(r);
if(L==R) {
for(int i=l;i<=r;i++) //暴力枚举这个块
a[i]=(a[i]+x)%MOD,sum[L]=(sum[L]+x)%MOD;
return;
}
for(int i=L+1;i<R;i++) sum[i]=(sum[i]+x*S)%MOD,add[i]=(add[i]+x)%MOD;
//中间块的枚举
for(int i=l;i<L*S;i++) a[i]=(a[i]+x)%MOD,sum[kuai(i)]=(sum[kuai(i)]+x)%MOD;
//左边到块末尾的枚举
for(int i=(R-1)*S;i<=r;i++) a[i]=(a[i]+x)%MOD,sum[kuai(i)]=(sum[kuai(i)]+x)%MOD;
//块开始到右边的枚举
}
inline ll query(int l,int r) {
ll ans=0; int L=kuai(l),R=kuai(r);
if(L==R) {
for(int i=l;i<=r;i++) ans=(ans+a[i]+add[L])%MOD; //暴力枚举这个块
return ans;
}
for(int i=L+1;i<R;i++) ans=(ans+sum[i])%MOD;
//中间块的枚举
for(int i=l;i<=L*S-1;i++) ans=(ans+a[i]+add[kuai(i)])%MOD;
//左边到块末尾的枚举
for(int i=(R-1)*S;i<=r;i++) ans=(ans+a[i]+add[kuai(i)])%MOD;
//块开始到右边的枚举
return ans;
}
int main(){
n=read(); m=read(); S=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read(),sum[kuai(i)]=(sum[kuai(i)]+a[i])%MOD;
while(m--) {
int opt=read(),l=read(),r=read();
if(opt==2) printf("%lld
",query(l,r));
else {
int x=read();
update(l,r,x);
}
}
return 0;
}