分块 (学习笔记)(25.11.18)

分块 (学习笔记)

概述

分块是一种思想,而并非一种数据结构,其思想是把一个序列去分成不同小块,然后预处理每个小块,在枚举的时候可以减小时间复杂度,其实就是一种暴力优化,与线段树有点类似

分块求区间加法

以下将分为两种代码,一种是类似与线段树建树,有一个建块部分,可以清楚的表示出每个点所属的块编号,每个块的大小等,但是常数更大。还有一种是直接通过数学计算可以计算出某个点属于哪个块直接进行查询和修改

建块

//分块求区间加
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5*3+100;
#define ll long long
int n;
ll a[N];
//分别表示块长,块的数量,某个点属于哪个块
int B, cnt, pos[N];
struct obj {
 int l, r;
}chunk[N];//一个块的左右节点
void build() {
 B = (long long)sqrt(n);//块长赋为根号N
 while (1) {
     ++cnt;//去增加一个块
     chunk[cnt].l = (cnt-1)*B+1;//左端点是上一个的右端点+1
     chunk[cnt].r = cnt*B;
     if (chunk[cnt].r>=n) {
         chunk[cnt].r = n;//如果到了边界外就取更小值
         break;
     }
 }
 for (int i=1; i<=cnt; i++) {
     //然后每个点去做处理看一下属于哪个块
     for (int j=chunk[i].l; j<=chunk[i].r; ++j) {
         pos[j] = i;
     }
 }
}
ll tag[N];
void update(ll l, ll r, ll x) {
 if (pos[l]==pos[r]) {
     //属于一个块就暴力
     for (int i=l; i<=r; ++i) a[i]+=x;
     return ;
 }
 //否则就块左,右分别暴力,在去给块加懒标记
 for (int i=l; i<=chunk[pos[l]].r; ++i) a[i]+=x;
 for (int i=chunk[pos[r]].l;i<=r;++i) a[i]+=x;
 for (int i=pos[l]+1; i<pos[r]; ++i) tag[i]+=x;
}
ll query(ll x) {
 //查询一个点,记得加上懒标记
 return a[x]+tag[pos[x]];
}
int main() {
 ios::sync_with_stdio(0), cin.tie(0);

 cin>>n;
 for (int i=1; i<=n; i++) {
     cin>>a[i];
 }build();//记得建树
 for (int i=1; i<=n; i++) {
     int x;ll l, r, c;
     cin>>x;
     if (x==0) {
         cin>>l>>r>>c;
         update(l,r,c);
     }
     if (x==1) {
         cin>>l>>r>>c;
         cout<<(long long)query(r)<<'\n';
     }
 }
 return 0;
}

无需建块,直接计算


posted @ 2025-11-18 17:46  Yuriha  阅读(2)  评论(0)    收藏  举报