Preface

分块,一个神奇的暴力算法。可以把很多\(O(n^2)\)的数据结构题的暴力优化到常数极小的\(O(n\sqrt n)\)。当一些毒瘤题无法用线段树,主席树,平衡树,树状数组......等\(O(n\ logn)\)方法写出时当然在你不会写这些算法的时候用大力分块骗分是再好不过的方法了!

当然分块的大致思想比较简单,我们看一下:

/Here is Pic 1

但是由于分块在实际应用中有不同的方法,所以让我们来找各种各样的板子题来练练手吧。


数列分块入门 1——区间加法,单点查值

这道题对于几乎所有的线性数据结构来说,这应该算是一个必修的操作了吧。

对于分块,我们一般就考虑两个问题:

  1. 对于块内的元素如何高效地操作(\(O(1)\)或\(O(log\ n)\))
  2. 对于快外的元素怎么暴力处理(\(O(\sqrt n)\)或\(O(log\ \sqrt n)\))

对于第一个问题,我们对每个完整的块打一个标记,然后两端不完整的部分我们直接遍历更新即可。

查询的时候讲它本身的值和它所在块的标记相加即可。

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+'0');
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int query(int x)
{
return mark[blk[x]]+a[x];
}
inline void modify(int l,int r,int k)
{
register int i;
for (i=l;i<=min(blk[l]*size,r);++i) a[i]+=k;
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) a[i]+=k;
for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=k;
}
int main()
{
//freopen("1.in","r",stdin); freopen("1.out","w",stdout);
register int i; read(n); size=sqrt(n);
for (i=1;i<=n;++i)
read(a[i]),blk[i]=(i-1)/size+1;
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(y)),putchar('\n'); else modify(x,y,z);
}
return 0;
}

数列分块入门 2——区间加法,询问区间内小于某个值的元素个数

这道题其实在没有加法并且离线的情况下可以用主席树解决,但在这里似乎没有什么好的\(O(n\ logn)\)方法。

对于分块,区间加法的时候还是常规的块内打标记+块外暴力更新

但是询问小于某个值的元素个数又是什么鬼?

然后我们就发现,分块对于一般数据结构的优势就来了,我们可以对每一块进行排序

然后查询,二分了解一下。不过虽然修改的时候对于整块的顺序没有影响,但是我们对于两端的暴力就会影响相对顺序。

然后处理方法也很暴力,直接重新排序即可,复杂度约为\(O(n\sqrt n\cdot log\sqrt n)\)

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z,tot;
vector <int> r[BLO];
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+'0');
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int find(int id,int x)
{
int L=0,R=size-1,res=-1;
while (L<=R)
{
int mid=L+R>>1;
if (r[id][mid]<x) res=mid,L=mid+1; else R=mid-1;
}
return res+1;
}
inline void reset(int id)
{
register int i; r[id].clear();
for (i=(id-1)*size+1;i<=id*size;++i)
r[id].push_back(a[i]);
sort(r[id].begin(),r[id].end());
}
inline void modify(int l,int r,int x)
{
register int i;
for (i=l;i<=min(blk[l]*size,r);++i)
a[i]+=x; reset(blk[l]);
if (blk[l]!=blk[r])
{
for (i=(blk[r]-1)*size+1;i<=r;++i)
a[i]+=x; reset(blk[r]);
}
for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=x;
}
inline int query(int l,int r,int x)
{
register int i; int ans=0;
for (i=l;i<=min(blk[l]*size,r);++i) (a[i]+mark[blk[l]]<x)&&++ans;
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) (a[i]+mark[blk[r]]<x)&&++ans;
for (i=blk[l]+1;i<=blk[r]-1;++i) ans+=find(i,x-mark[i]);
return ans;
}
int main()
{
//freopen("2.in","r",stdin); freopen("2.out","w",stdout);
register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
for (i=1;i<=n;++i)
read(a[i]),r[blk[i]=(i-1)/size+1].push_back(a[i]);
for (i=1;i<=tot;++i)
sort(r[i].begin(),r[i].end());
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(x,y,z*z)),putchar('\n'); else modify(x,y,z);
}
return 0;
}

数列分块入门 3——区间加法,询问区间内小于某个值的前驱

这道题有了上一道题的思路就很简单了。

几乎是一样的方法我是直接改了下上面的代码的,只不过是二分的几个细节改了下而已

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005,BLO=320;
int n,a[N],size,blk[N],mark[BLO],opt,x,y,z,tot;
vector <int> r[BLO];
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
if (x<0) putchar('-'),x=-x;
if (x>9) write(x/10); putchar(x%10+'0');
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int max(int a,int b)
{
return a>b?a:b;
}
inline int find(int id,int x)
{
int L=0,R=size-1,res=-1;
while (L<=R)
{
int mid=L+R>>1;
if (r[id][mid]<x) res=r[id][mid],L=mid+1; else R=mid-1;
}
return ~res?res+mark[id]:-1;
}
inline void reset(int id)
{
register int i; r[id].clear();
for (i=(id-1)*size+1;i<=id*size;++i)
r[id].push_back(a[i]);
sort(r[id].begin(),r[id].end());
}
inline void modify(int l,int r,int x)
{
register int i;
for (i=l;i<=min(blk[l]*size,r);++i)
a[i]+=x; reset(blk[l]);
if (blk[l]!=blk[r])
{
for (i=(blk[r]-1)*size+1;i<=r;++i)
a[i]+=x; reset(blk[r]);
}
for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=x;
}
inline int query(int l,int r,int x)
{
register int i; int ans=-1;
for (i=l;i<=min(blk[l]*size,r);++i) (a[i]+mark[blk[l]]<x)&&(ans=max(ans,a[i]+mark[blk[l]]));
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) (a[i]+mark[blk[r]]<x)&&(ans=max(ans,a[i]+mark[blk[r]]));
for (i=blk[l]+1;i<=blk[r]-1;++i) ans=max(ans,find(i,x-mark[i]));
return ans;
}
int main()
{
//freopen("3.in","r",stdin); freopen("3.out","w",stdout);
register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
for (i=1;i<=n;++i)
read(a[i]),r[blk[i]=(i-1)/size+1].push_back(a[i]);
for (i=1;i<=tot;++i)
sort(r[i].begin(),r[i].end());
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(x,y,z)),putchar('\n'); else modify(x,y,z);
}
return 0;
}

数列分块入门九题(一):LOJ6277~6279的更多相关文章

  1. 数列分块入门九题(三):LOJ6283~6285

    Preface 最后一题我一直觉得用莫队是最好的. 数列分块入门 7--区间乘法,区间加法,单点询问 还是很简单的吧,比起数列分块入门 7就多了个区间乘. 类似于线段树,由于乘法的优先级高于加法,因此 ...

  2. 数列分块入门九题(二):LOJ6280~6282

    Preface 个人感觉这中间的三题是最水的没有之一 数列分块入门 4--区间加法,区间求和 这个也是很多数据结构完爆的题目线段树入门题,但是练分块我们就要写吗 修改还是与之前类似,只不过我们要维护每 ...

  3. LOJ6285 数列分块入门9(分块)

    昨天对着代码看了一晚上 然后今天终于在loj上过了 数列分块入门9题撒花★,°:.☆( ̄▽ ̄)/$:.°★ . 然后相当玄学 块的大小调成\(\sqrt{n}\)会TLE,改成150就过了 啧 然后就 ...

  4. LOJ #6279. 数列分块入门 3-分块(区间加法、查询区间内小于某个值x的前驱(比其小的最大元素))

    #6279. 数列分块入门 3 内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 3   题目描述 给 ...

  5. LOJ6277~6285 数列分块入门

    Portals 分块需注意的问题 数组大小应为,因为最后一个块可能会超出的范围. 当操作的区间在一个块内时,要特判成暴力修改. 要清楚什么时候应该+tag[t] 数列分块入门 1 给出一个长为的数列, ...

  6. loj 6278 6279 数列分块入门 2 3

    参考:「分块」数列分块入门1 – 9 by hzwer 2 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,询问区间内小于某个值\(x\)的元素个数. 思 ...

  7. 数列分块入门1-9 By hzwer

    声明 持续更新,因为博主也是正在学习分块的知识,我很菜的,菜的抠$jio$ 写在前面 分块是个很暴力的算法,但却比暴力优秀的多,分块算法的时间复杂度一般是根号的,他的主要思想是将一个长度是$n$的数列 ...

  8. LOJ——#6277. 数列分块入门 1

    ~~推荐播客~~ 「分块」数列分块入门1 – 9 by hzwer 浅谈基础根号算法——分块 博主蒟蒻,有缘人可直接观摩以上大佬的博客... #6277. 数列分块入门 1 题目大意: 给出一个长为 ...

  9. [Loj] 数列分块入门 1 - 9

    数列分块入门 1 https://loj.ac/problem/6277 区间加 + 单点查询 #include <iostream> #include <cstdio> #i ...

随机推荐

  1. Vue父组件接收不到子组件$emit事件的原因分析

    通常有两种情况: 事件名称不全是小写.事件名称要求全小写. 不是父子关系.这里的父子关系是严格的父子关系,祖孙关系也不行.只能一层一层触发,这在写树形组件时,很容易掉坑里.

  2. CSS中各种长度单位总结

    在前端开发工作过程中曾碰到这样一问题: <style type="text/css"> .parent{ width:400px; height:300px; bord ...

  3. centos 7.2 64位 docker安装lamp环境

    1. 删除docker可能有的早期版本 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docke ...

  4. [20171223]grid用户的环境变量问题.txt

    [20171223]grid用户的环境变量问题.txt --//oracle 11G 安装RAC,一般需要建立grid用户,使用这个用户管理asm,群集信息.--//在安装过程中,同事的疑问实际上也是 ...

  5. 洗礼灵魂,修炼python(47)--巩固篇—定义类的方法之@classmethod,@staticmethod

    定义类的方法,相信你会说,不就是在class语句下使用def () 就是定义类的方法了嘛,是的,这是定义的方法的一种,而且是最普通的方式 首先,我们已经知道有两种方式: 1.普通方法: 1)与类无关的 ...

  6. 在VS 一切正常,发布到IIS出现问题 [System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本]

    在VS 一切正常,发布到IIS出现问题 [System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本] 前提条件: 在vs 开发的时候,一 ...

  7. 【PAT】B1066 图像过滤(15 分)

    注意输出是占三位,其他的挺水 #include<stdio.h> #include<algorithm> using namespace std; int main(){ in ...

  8. JSON语法规则

    JSON 语法规则 JSON 语法是 JavaScript 对象表示法语法的子集. 数据在名称/值对中 数据由逗号分隔 花括号保存对象 方括号保存数组 JSON 名称/值对 JSON 数据的书写格式是 ...

  9. 【Ansible 文档】【译文】Playbooks 变量

    Variables 变量 自动化的存在使得重复的做事情变得很容易,但是我们的系统不可能完全一样. 在某些系统中,你可能想要设置一些与其他系统不一样的行为和配置. 同样地,远程系统的行为和状态也可以影响 ...

  10. visual studio code前端插件及常用快捷键【转】

    通用插件 HTML Snippets 超级实用且初级的 H5代码片段以及提示 HTML CSS Support 让 html 标签上写class 智能提示当前项目所支持的样式新版已经支持scss文件检 ...