【学习笔记】 分块

【学习笔记】分块

算法简介

分块是一种优雅的暴力,他的实现原理是将一段区间分割成一些长度相等的整块和一些散块进行暴力,有点类似线段树。
每一次操作时将区间分成中间的一些整块,和两端的一些散块。举一个例子,假设我们要对区间\([2,10]\)进行操作,假设整块的块长为\(3\),那么整个大区间就可以分为\([2,3],[4,6],[7,9],[10,10]\)这几个区间
进行操作。
再来分析复杂度,设块长为\(B\),区间总长为\(n\),则单次操作\(O(\frac{n}{b}+b)\),分别为处理散块时的复杂度,和处理整块时的复杂度,由基本不等式可得最优为\(O(\sqrt{n})\),总的最优为\(O(n\times \sqrt{n})\), 此时块长为\(\sqrt{n}\)

\(code\)

需要求出
1.每个元素所属块的编号
2.对于整个块的操作
3.对于单个元素的操作

#include<bits/stdc++.h>
#define maxn 100010
#define ll long long
using namespace std;
int n, m, sz, tot = 0;
ll L[maxn], R[maxn], add[maxn], sum[maxn], a[maxn];
int bel[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	} 
	sz = sqrt(n);
	if(n % sz) tot = n / sz + 1;
	else tot = n / sz;
	for(int i = 1; i <= tot; i++){
		L[i] = sz * (i - 1) + 1;
		R[i] = min(sz * i, n);
	}
	for(int i = 1; i <= n; i++){
		bel[i] = (i - 1) / sz + 1;
		sum[bel[i]] += a[i]; 
	}
	for(int i = 1; i <= m; i++){
		ll op, l, r, k;
		cin >> op >> l >> r;
		if(op == 1){
			cin >> k;
			if(bel[l] == bel[r]){
				for(int i = l; i <= r; i++){
					sum[bel[i]] += k;
					a[i] += k;
				}
			}
			else{
				for(int i = bel[l] + 1; i < bel[r]; i++){
					sum[i] += sz * k;
					add[i] += k;
				}
				for(int i = l; i <= R[bel[l]]; i++){
					a[i] += k;
					sum[bel[i]] += k;
				}
				for(int i = L[bel[r]]; i <= r; i++){
					a[i] += k;
					sum[bel[i]] += k;
				}
			}
		}
		else{
			ll ans = 0;
			if(bel[l] == bel[r]){
				for(int i = l; i <= r; i++){
					ans += a[i] + add[bel[i]];
				}
			}
			else{
				for(int i = bel[l] + 1; i < bel[r]; i++){
					ans += sum[i]; 
				}

				for(int i = l; i <= R[bel[l]]; i++){
					ans += a[i] + add[bel[i]];
				}
				for(int i = L[bel[r]]; i <= r; i++){
					ans += a[i] + add[bel[i]];
				}
			}
			cout << ans << endl;
		}
	}
	return 0;
}
posted @ 2024-11-26 17:21  GuoSN0410  阅读(34)  评论(0)    收藏  举报