数列分块入门九题(一):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 ...
随机推荐
- c#所有部门及其下所部门生成树形图(递归算法获取或键值对方式获取)
部门数据库的设计: 代码: /// <summary> /// 获取部门(入口) /// </summary> /// <returns></returns& ...
- 关于使用WeUI在IE中提示“font-face 未能完成 OpenType 嵌入权限检查。权限必须是可安装的。”的问题
@font-face是css3中定义字体的规则. 首先,在使用weui时,在Chrome.Firefox下没有问题,但是在IE下提示“font-face 未能完成 OpenType 嵌入权限检查.权限 ...
- Linux源码解析-内核栈与thread_info结构详解
1.什么是进程的内核栈? 在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈 2.进程的内核栈在计算机中是如何描 ...
- pkg-config 用法
在进行使用fuse 2.9写程序的时候,遇到了pkg-config 命令和 .pc 文件.本篇博客就具体说明一下pkg-config 命令是什么? 我们首先看一下: gcc -Wall hello.c ...
- Business talking in English
Talking one: A: Microsoft, this is Steve. B: Hi Steve, this is Richard from Third Hand Testing. I am ...
- nginx代理 upstream轮询
问题描述 我有2个Tomcat 一个端口开启(8021),一个端口未开启(8022),在nginx里用upstream模块进行代理 ,代理的负载算法采用的是默认的轮询算法,配置成功后,访问页面时并没 ...
- ELK-elasticsearch-6.3.2插件【head,bigdesk,cerebro[kopf]】安装
参考博客:linux下ElasticSearch.6.2.2集群安装与head.Kibana.X-Pack..插件的配置安装 参考博客:ELK5.5.1 插件安装实践纪要(head/bigdesk/k ...
- SAP SQVI 快速浏览器
SQVI可向SQL一样连接多个表浏览数据. 1.输入T-CODE:SQVI. 2.新建一个新查询case 输入CASE 名.点击新建,在弹出的窗口中输入标题,在数据源中可选择单个表查询,或者选择表连接 ...
- VRS——备忘
1.所有版本VRS,注释掉m_ChisauarePRN参数 2.注释掉所有的MODULE_GNSSMonitor_ID,暂时用不到.但是会造成basestation崩掉. n.RegisterStat ...
- SQL2008 一直error40 无法连接到localhost
1. Problem 2. Reason 可能是之前卸载SQL Server时没卸载干净 后来又重新安装时导致默认实例名不能用 就随手写了个SQLMOLORY实例名 但其实系统内这时是有两个SQL实例 ...