数列分块入门九题(二):LOJ6280~6282
Preface
个人感觉这中间的三题是最水的没有之一
数列分块入门 4——区间加法,区间求和
这个也是很多数据结构完爆的题目线段树入门题,但是练分块我们就要写吗
修改还是与之前类似,只不过我们要维护每一块内元素的和,注意这个要实时更新
这样就可以轻松水过了。
CODE
#include<cstdio>
#include<cctype>
#include<cmath>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],blk[N],size,opt,x,y,z;
long long mark[BLO],sum[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()));
}
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 l,int r,int mod)
{
register int i; int res=0;
for (i=l;i<=min(blk[l]*size,r);++i) res=(res+1LL*(a[i]+mark[blk[l]]))%mod;
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res=(res+1LL*(a[i]+mark[blk[r]]))%mod;
for (i=blk[l]+1;i<=blk[r]-1;++i) res=(res+1LL*sum[i])%mod;
return res;
}
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,sum[blk[l]]+=x;
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i)
a[i]+=x,sum[blk[r]]+=x;
for (i=blk[l]+1;i<=blk[r]-1;++i)
mark[i]+=x,sum[i]+=x*size;
}
int main()
{
//freopen("4.in","r",stdin); freopen("4.out","w",stdout);
register int i; read(n); size=(int)sqrt(n);
for (i=1;i<=n;++i)
read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i];
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(x,y,z+1)),putchar('\n'); else modify(x,y,z);
}
return 0;
}
数列分块入门 5——区间开方,区间求和
这道题其实也是一道经典的并查集的题目,但是我们只讲分块。
首先我们要注意到,一个数被进行开方操作至多\(O(log^2n)\)次时它就会变成\(0/1\)
然后我们对于每一个块在维护和的同时,在打上一个标记,当一个块内所有元素都是\(0/1\)时就不更新它。
否则暴力搞一遍即可。最后注意修改时记得把区间和的标记一起维护不能直接开方
CODE
#include<cstdio>
#include<cctype>
#include<cmath>
using namespace std;
const int N=50005,BLO=250;
int n,a[N],blk[N],sum[BLO],size,opt,x,y,z;
bool flag[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()));
}
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 void reset(int x)
{
register int i; flag[x]=1;
for (i=(x-1)*size+1;i<=x*size;++i)
{
sum[x]-=a[i]; a[i]=sqrt(a[i]); sum[x]+=a[i];
if (a[i]>1) flag[x]=0;
}
}
inline int query(int l,int r)
{
register int i; int res=0;
for (i=l;i<=min(blk[l]*size,r);++i) res+=a[i];
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res+=a[i];
for (i=blk[l]+1;i<=blk[r]-1;++i) res+=sum[i];
return res;
}
inline void modify(int l,int r)
{
register int i;
for (i=l;i<=min(blk[l]*size,r);++i)
sum[blk[l]]-=a[i],a[i]=sqrt(a[i]),sum[blk[l]]+=a[i];
if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i)
sum[blk[r]]-=a[i],a[i]=sqrt(a[i]),sum[blk[r]]+=a[i];
for (i=blk[l]+1;i<=blk[r]-1;++i) if (!flag[i]) reset(i);
}
int main()
{
//freopen("5.in","r",stdin); freopen("5.out","w",stdout);
register int i; read(n); size=sqrt(n);
for (i=1;i<=n;++i)
read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i];
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(x,y)),putchar('\n'); else modify(x,y);
}
return 0;
}
数列分块入门 6——单点插入,单点询问
首先要正确理解题意,然后我们发现这个和分块有个毛线关系。
我们注意到对于使用数组模拟这个过程时,查询是\(O(1)\)的,但在同时插入时是\(O(n)\)的
那么我们思考如何权衡这个问题,我们也可以进行分块。
只不过这里的分块就是对于每块内开一个vector,然后你要知道有一个insert函数真是超级好用
然后我们在插入时可以做到\(O(\sqrt n)\),在数据随机的情况下表现优异。
CODE
#include<cstdio>
#include<cctype>
#include<cmath>
#include<vector>
#define pb push_back
using namespace std;
const int N=200005,BLO=450;
int n,size,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()));
}
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)
{
for (register int i=1;i<=tot;++i)
if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size();
}
inline void insert(int x,int y)
{
for (register int i=1;i<=tot;++i)
if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size();
}
int main()
{
//freopen("a.in","r",stdin); freopen("a.out","w",stdout);
register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
for (i=1;i<=n;++i)
read(x),r[(i-1)/size+1].pb(x);
for (i=1;i<=n;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(y)),putchar('\n'); else insert(x,y);
}
return 0;
}
不过上面的做法也有一定的缺陷,如果出题人就是要卡你,只需要一直出在同一位置插入的数据即可。
然后最坏情况下就变成\(O(n^2)\)暴力了,然后我们引进重新分块这样的概念
一种重构类似于替罪羊树式重构,当一个块内的元素特别多(可以设临界值)时,直接暴力把这个块裂成两半即可。
但是我更喜欢一个超级粗暴的方式:每做\(\sqrt n\)次操作后,直接重新分块(全部for过去),复杂度和大体的一致,也是\(O(n\sqrt n)\)的。
这样就卡不了你了但是在随机数据下被不重构的分块吊打了
CODE
#include<cstdio>
#include<cctype>
#include<cmath>
#include<vector>
#define pb push_back
using namespace std;
const int N=200005,BLO=450;
int n,size,opt,cur[N],x,y,z,tot,q;
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()));
}
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 void rebuild(void)
{
register int i,j; n=0;
for (i=1;i<=tot;++i)
{
for (j=0;j<r[i].size();++j)
cur[++n]=r[i][j]; r[i].clear();
}
size=sqrt(n); tot=(n-1)/size+1;
for (i=1;i<=n;++i)
r[(i-1)/size+1].pb(cur[i]);
}
inline int query(int x)
{
for (register int i=1;i<=tot;++i)
if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size();
}
inline void insert(int x,int y)
{
for (register int i=1;i<=tot;++i)
if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size();
}
int main()
{
//freopen("6.in","r",stdin); freopen("6.out","w",stdout);
register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
for (i=1;i<=n;++i)
read(x),r[(i-1)/size+1].pb(x);
for (q=n,i=1;i<=q;++i)
{
read(opt); read(x); read(y); read(z);
if (opt) write(query(y)),putchar('\n'); else insert(x,y);
if (i%size==0) rebuild();
}
return 0;
}
数列分块入门九题(二):LOJ6280~6282的更多相关文章
- 数列分块入门九题(三):LOJ6283~6285
Preface 最后一题我一直觉得用莫队是最好的. 数列分块入门 7--区间乘法,区间加法,单点询问 还是很简单的吧,比起数列分块入门 7就多了个区间乘. 类似于线段树,由于乘法的优先级高于加法,因此 ...
- 数列分块入门九题(一):LOJ6277~6279
Preface 分块,一个神奇的暴力算法.可以把很多\(O(n^2)\)的数据结构题的暴力优化到常数极小的\(O(n\sqrt n)\).当一些毒瘤题无法用线段树,主席树,平衡树,树状数组...... ...
- LOJ6285 数列分块入门9(分块)
昨天对着代码看了一晚上 然后今天终于在loj上过了 数列分块入门9题撒花★,°:.☆( ̄▽ ̄)/$:.°★ . 然后相当玄学 块的大小调成\(\sqrt{n}\)会TLE,改成150就过了 啧 然后就 ...
- LOJ #6282. 数列分块入门 6-分块(单点插入、单点查询、数据随机生成)
#6282. 数列分块入门 6 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 1 题目描述 给出 ...
- LOJ——#6277. 数列分块入门 1
~~推荐播客~~ 「分块」数列分块入门1 – 9 by hzwer 浅谈基础根号算法——分块 博主蒟蒻,有缘人可直接观摩以上大佬的博客... #6277. 数列分块入门 1 题目大意: 给出一个长为 ...
- [Loj] 数列分块入门 1 - 9
数列分块入门 1 https://loj.ac/problem/6277 区间加 + 单点查询 #include <iostream> #include <cstdio> #i ...
- LOJ #6281. 数列分块入门 5-分块(区间开方、区间求和)
#6281. 数列分块入门 5 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 5 题目描述 给出 ...
- loj 6278 6279 数列分块入门 2 3
参考:「分块」数列分块入门1 – 9 by hzwer 2 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,询问区间内小于某个值\(x\)的元素个数. 思 ...
- 数列分块入门1-9 By hzwer
声明 持续更新,因为博主也是正在学习分块的知识,我很菜的,菜的抠$jio$ 写在前面 分块是个很暴力的算法,但却比暴力优秀的多,分块算法的时间复杂度一般是根号的,他的主要思想是将一个长度是$n$的数列 ...
随机推荐
- 《ASP.NET MVC企业实战》(三)MVC开发前奏
在上一篇“<ASP.NET MVC企业级实战>(二)MVC开发前奏”中跟随作者大概了解了一些C#3.0和3.5中的新特性.本篇继续以这样的方式来学习C#中的一些特性. 一.C#3. ...
- Flume Source 实例
Flume Source 实例 Avro Source 监听avro端口,接收外部avro客户端数据流.跟前面的agent的Avro Sink可以组成多层拓扑结构. 1 2 3 4 5 6 7 8 9 ...
- Fiddler抓包使用教程-过滤
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72929800 本文出自[赵彦军的博客] Fiddler抓包可以完成我们移动开发者的 ...
- 【Java入门提高篇】Day24 Java容器类详解(七)HashMap源码分析(下)
前两篇对HashMap这家伙的主要方法,主要算法做了一个详细的介绍,本篇主要介绍HashMap中默默无闻地工作着的集合们,包括KeySet,values,EntrySet,以及对应的迭代器:HashI ...
- SpringBoot整合定时任务
定时任务一般是项目中都需要用到的,可以用于定时处理一些特殊的任务. 在SpirngBoot中使用定时任务变的特别简单,不需要再像SpringMVC一样写很多的配置,只需要在启动类上增加一个@Enabl ...
- aws linux主机root帐号登录
默认情况下,aws主机必须使用pem密码文件并且以ec2-user用户登录系统,之后很多操作都必须用sudo来以root权限执行操作,显得比较麻烦. 以下来自知乎的一个问答,亲测ok ## AWS E ...
- 洗礼灵魂,修炼python(33)--面向对象编程(3)—特殊类方法__init__,公有属性,私有属性
在上一篇博文里,传入参数时,是在实例化对象后且在调用方法时才传入参数,感觉是不是有点繁琐对吧?可以在实例化的时候就传入参数吗?可以的,这就是本篇博文的要讲到的构造器——__init__(两边双下划线) ...
- 第七章 鼠标(CONNECT)
/* CONNECT.C -- Connect-the-Dots Mouse Demo Program (c) Charles Petzold,1998 */ #include <Windows ...
- Python作业第一课
零基础开始学习,最近周边的同学们都在学习,我也来试试,嘿嘿,都写下来,下次不记得了还能来看看~~ Python作业第一课1)登陆,三次输入锁定,下次不允许登陆2)设计一个三级菜单,菜单内容可自行定义, ...
- Python进阶(一)
完成慕课网的python基础学习以后,大约花了三天时间,平均每天一个小时,总结了一些比较好的例题和思想方法,下面来学习python进阶吧 参考廖雪峰官方课程 函数 python官方函数调用文档 定义默 ...