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. [20180408]那些函数索引适合字段的查询.txt

    [20180408]那些函数索引适合字段的查询.txt --//一般不主张建立函数索引,往往是开发的无知,使用trunc等函数,实际上一些函数也可以用于字段的查询.--//以前零碎的写过一些,放假看了 ...

  2. NetBeans数据库笔记---三层架构

    1.创建数据库,数据表 用MySQL数据库和Navicat for MySQL工具创建表 2.创建实体类——反应表结构(列——变量) 也就是对应表建立的gets和sets方法,实体类的名字一般都与数据 ...

  3. php开发中遇到的一些问题

    php警告提示A session had already been started – ignoring session_start() 解决方案 判断 如果session_id 不存在,说明没有储存 ...

  4. SAP ABAP 如何查找SMOD增强

    1.查找程序名 T-CODE:SE93 2.查找开发类 T-code:se38 3.查找SMOD增强 T-CODE:SE16N.表:TADIR 4.查看增强具有哪些功能 T-CODE:SE16N.表: ...

  5. 使用golang的slice来模拟栈

    slice(切片):底层数据结构是数组 stack(栈):一种先进后出的数据结构 普通版的模拟写入和读取的栈 package main import "fmt" //栈的特点是先进 ...

  6. Java客户端连接kafka集群报错

    往kafka集群发送消息时,报错如下: page_visits-1: 30005 ms has passed since batch creation plus linger time 加入log4j ...

  7. jsp 一点点

    jsp学习 jsp -处理 作为正常的页面,你的浏览器发送一个http请求道web服务器. web 服务器承认一个JSP页面的HTTP请求,并将其转发给一个JSP引擎. JSP引擎从磁盘加载JSP页面 ...

  8. EBS-新增和更新价目表行

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/gh320/article/details/36666133  新增和更新价目表行 --目的:在已 ...

  9. NDK/JNI学习--环境搭建

    基于Windows来开发Android的NDK.JNI须要的环境搭建,所须要的软件例如以下: Android 开发环境的基本配置(SDk,ADT.Eclipse IDE.JDK)       这些都是 ...

  10. Spring Cloud Eureka 属性作用

    配置参数 默认值 说明 服务注册中心配置 Bean类:org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean eu ...