数列分块入门九题(一):LOJ6277~6279
Preface
分块,一个神奇的暴力算法。可以把很多\(O(n^2)\)的数据结构题的暴力优化到常数极小的\(O(n\sqrt n)\)。当一些毒瘤题无法用线段树,主席树,平衡树,树状数组......等\(O(n\ logn)\)方法写出时当然在你不会写这些算法的时候用大力分块骗分是再好不过的方法了!
当然分块的大致思想比较简单,我们看一下:
/Here is Pic 1
但是由于分块在实际应用中有不同的方法,所以让我们来找各种各样的板子题来练练手吧。
数列分块入门 1——区间加法,单点查值
这道题对于几乎所有的线性数据结构来说,这应该算是一个必修的操作了吧。
对于分块,我们一般就考虑两个问题:
- 对于块内的元素如何高效地操作(\(O(1)\)或\(O(log\ n)\))
- 对于快外的元素怎么暴力处理(\(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的更多相关文章
- 数列分块入门九题(三):LOJ6283~6285
Preface 最后一题我一直觉得用莫队是最好的. 数列分块入门 7--区间乘法,区间加法,单点询问 还是很简单的吧,比起数列分块入门 7就多了个区间乘. 类似于线段树,由于乘法的优先级高于加法,因此 ...
- 数列分块入门九题(二):LOJ6280~6282
Preface 个人感觉这中间的三题是最水的没有之一 数列分块入门 4--区间加法,区间求和 这个也是很多数据结构完爆的题目线段树入门题,但是练分块我们就要写吗 修改还是与之前类似,只不过我们要维护每 ...
- LOJ6285 数列分块入门9(分块)
昨天对着代码看了一晚上 然后今天终于在loj上过了 数列分块入门9题撒花★,°:.☆( ̄▽ ̄)/$:.°★ . 然后相当玄学 块的大小调成\(\sqrt{n}\)会TLE,改成150就过了 啧 然后就 ...
- LOJ #6279. 数列分块入门 3-分块(区间加法、查询区间内小于某个值x的前驱(比其小的最大元素))
#6279. 数列分块入门 3 内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 3 题目描述 给 ...
- LOJ6277~6285 数列分块入门
Portals 分块需注意的问题 数组大小应为,因为最后一个块可能会超出的范围. 当操作的区间在一个块内时,要特判成暴力修改. 要清楚什么时候应该+tag[t] 数列分块入门 1 给出一个长为的数列, ...
- loj 6278 6279 数列分块入门 2 3
参考:「分块」数列分块入门1 – 9 by hzwer 2 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,询问区间内小于某个值\(x\)的元素个数. 思 ...
- 数列分块入门1-9 By hzwer
声明 持续更新,因为博主也是正在学习分块的知识,我很菜的,菜的抠$jio$ 写在前面 分块是个很暴力的算法,但却比暴力优秀的多,分块算法的时间复杂度一般是根号的,他的主要思想是将一个长度是$n$的数列 ...
- LOJ——#6277. 数列分块入门 1
~~推荐播客~~ 「分块」数列分块入门1 – 9 by hzwer 浅谈基础根号算法——分块 博主蒟蒻,有缘人可直接观摩以上大佬的博客... #6277. 数列分块入门 1 题目大意: 给出一个长为 ...
- [Loj] 数列分块入门 1 - 9
数列分块入门 1 https://loj.ac/problem/6277 区间加 + 单点查询 #include <iostream> #include <cstdio> #i ...
随机推荐
- Angular基础(五) 内建指令和表单
Angular提供了一些内建的指令,可以作为属性添加给HTML元素,以动态控制其行为. 一.内建指令 a) *ngIf,可以根据条件来显示或隐藏HTML元素. <div *ngIf='a&g ...
- flask day01
目标:搭建好一个flask架构,并且可以运行起来,能够访问 ## 一丶配置环境 比较简单,只需要配一个flask pip3 install flask #也可以使用pip install flask ...
- [Linux.NET]Nginx 泛解析配置请求映射到多端口实现二级域名访问
由于想实现一个域名放置多个应用运行的目的,而不想通过域名后加端口号方式处理,这种方式处理记起来太麻烦,偷懒党简直不能忍,故而考虑了使用二级域名来处理多个应用同时运行.Google了一番资料并进行了尝试 ...
- 简述 Spring Cloud 是什么2
一.概念定义 Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案. Spring Cloud对微服务基础框架Ne ...
- 将mssql数据库高版本迁移到低版本
将mssql数据库高版本迁移到低版本 在低版本目标数据库中创建目标空数据库[TargetDb] ,注意新建数据库即可,不要创建任何表 在低版本数据库中,选中[服务器对象=>链接服务器] 右键[新 ...
- C#使用Aforge调用摄像头拍照
一. 新建一个Winform项目 二.使用Nuget添加引用 安装下图中红色框住的两个程序包 安装完后发现安装了如下图的程序包,这是因为上述两个程序包存在对其它程序包的依赖. 三.编写程序 1. 窗体 ...
- 用Python实现数据结构之链表
链表 链表与栈,队列不一样,它是由一个个节点构成的,每个节点存储着本身的一些信息,也存储着其他一个或多个节点的引用,可以从一个节点找到其他的节点,节点与节点之间就像是有链连在一起一样,这种数据结构就叫 ...
- 【第一章】zabbix3.4监控WindowsCPU使用率磁盘IO磁盘事件日志监控阈值邮件报警详细配置
Windows安装zabbix-agent 监控Windows-CPU使用率 监控Windows-磁盘IO性能监控 监控Windows/Linux-磁盘触发器阈值更改 监控Windows-网卡自动发现 ...
- Gogs基本使用介绍
Gogs简介 Gogs 是一款类似GitHub的开源文件/代码管理系统(基于Git),Gogs 的目标是打造一个最简单.最快速和最轻松的方式搭建自助 Git 服务.使用 Go 语言开发使得 Gogs ...
- U890采购入库单修改供应商
采购入库单表头 SELECT *FROM RdRecordWHERE (cCode = '0000051801') 采购入库单表体 SELECT *FROM RdRecordsWHERE (cPOID ...