数列分块入门九题(一):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 ...
随机推荐
- Cannot find wrapper assembly for type library "ADODB". in VS2017
Delete Microsoft ActiveX Data Objects {version} Library and then add it back. After resolving the pr ...
- 第 16 章 C 预处理器和 C 库(直角坐标转换极坐标)
/*------------------------------------- rect_pol.c -- 把直角坐标转换为极坐标 ---------------------------------- ...
- January 30th, 2018 Week 05th Tuesday
The things you own end up owning you. 你占有的东西终将会占有你. When we are longing for something, we would be w ...
- [USACO09JAN]安全出行Safe Travel
题目 什么神仙题啊,我怎么只会\(dsu\)啊 我们考虑一个非常暴力的操作,我们利用\(dsu\ on \ tree\)把一棵子树内部的非树边都搞出来,用一个堆来存储 我们从堆顶开始暴力所有的边,如果 ...
- Semaphore实现的生产者消费者程序
Semaphore:Semaphores are often used to restrict the number of threads than can access some (physical ...
- 转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁
[ZooKeeper.net] 3 ZooKeeper的分布式锁 基于ZooKeeper的分布式锁 源码分享:http://pan.baidu.com/s/1miQCDKk ZooKeeper ...
- docker swarm英文文档学习-8-在集群中部署服务
Deploy services to a swarm在集群中部署服务 集群服务使用声明式模型,这意味着你需要定义服务的所需状态,并依赖Docker来维护该状态.该状态包括以下信息(但不限于): 应该运 ...
- day12 Python列表
list#类 列表概括 li = [1,2,13,["石振文",["19", 10],"庞麦郎"],"charon",& ...
- linux安装jenkins和编译发布gitlib项目
1. jenkins下载地址 wget https://updates.jenkins-ci.org/download/war/2.138/jenkins.war 2. 安装插件 安装gitlib和m ...
- 扫盲记-第六篇--Normalization
深度学习模型中的Normalization 数据经过归一化和标准化后可以加快梯度下降的求解速度,这就是Batch Normalization等技术非常流行的原因,Batch Normalization ...