分块学习笔记

分块方法:

(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;
}
简易的代码胜过复杂的说教。
原文地址:https://www.cnblogs.com/bifanwen/p/12498884.html