博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数列分块入门(最后一个难啊)
阅读量:6221 次
发布时间:2019-06-21

本文共 11707 字,大约阅读时间需要 39 分钟。

感觉这个不算科技吧,就是现在把操作降在块内了,算是暴力的优化

LOJ6277. 数列分块入门 1

对于这些数你有两种操作,0是区间+,1是区间查询

#include
using namespace std;#define N 50005int m,belong[N];int a[N],tag[N];void change(int l,int r,int val){ for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val;//将a块内剩余部分暴力更新 if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val;//将b块内剩余部分暴力更新 } for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去}int main(){ int n; scanf("%d",&n); m=sqrt(n);//一块有几个 for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内 for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1,op,l,r,val;i<=n;i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)change(l,r,val); else printf("%d\n",tag[belong[r]]+a[r]);//tag获取这个序列被加的值 } return 0;}

LOJ6278. 数列分块入门 2

这个还是区间加,但是要查询区间小于c*c的值

这个就麻烦多了,还要排序的啊

#include
using namespace std;#define N 50005int n,m,belong[N];int a[N],tag[N];vector
vec[N];void reset(int x){ //将两个特殊的块更新 vec[x].clear(); for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]); sort(vec[x].begin(),vec[x].end());}void change(int l,int r,int val){ for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新 reset(belong[l]); if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新 reset(belong[r]); } for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去}int query(int l,int r,int val){ int ans=0; for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]

 

LOJ6279. 数列分块入门 3

询问区间内小于某个值 x的前驱(比其小的最大元素)

这个上面改改就行了

#include
using namespace std;#define N 100005int n,m,belong[N];int a[N],tag[N];vector
vec[N];void reset(int x){ //将两个特殊的块更新 vec[x].clear(); for(int i=(x-1)*m+1; i<=min(x*m,n); i++)vec[x].push_back(a[i]); sort(vec[x].begin(),vec[x].end());}void change(int l,int r,int val){ for(int i=l; i<=min(r,belong[l]*m); i++) a[i]+=val; //将a块内剩余部分暴力更新 reset(belong[l]); if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1; i<=r; i++) a[i]+=val; //将b块内剩余部分暴力更新 reset(belong[r]); } for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去}int query(int l,int r,int val){ int ans=-1; for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]
=1) ans=max(ans,vec[i][pos-1]+tag[i]); } return ans;}int main(){ scanf("%d",&n); m=sqrt(n);//一块有几个 for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内 for(int i=1; i<=n; i++)scanf("%d",&a[i]),vec[belong[i]].push_back(a[i]); for(int i=1;i<=belong[n];i++)sort(vec[i].begin(),vec[i].end());//每块要先保证有序,因为以后不更新了 for(int i=1,op,l,r,val; i<=n; i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)change(l,r,val); else printf("%d\n",query(l,r,val)); } return 0;}

 当然也可以用set去维护这个块,那个lowwerbound不要用错

#include
#include
using namespace std;#define N 100005int n,m,belong[N];int a[N],tag[N];set
S[N];void change(int l,int r,int val){ for(int i=l; i<=min(r,belong[l]*m); i++) { S[belong[l]].erase(a[i]); a[i]+=val; //将a块内剩余部分暴力更新 S[belong[l]].insert(a[i]); } if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1; i<=r; i++) { S[belong[r]].erase(a[i]); a[i]+=val; //将b块内剩余部分暴力更新 S[belong[r]].insert(a[i]); } } for(int i=belong[l]+1; i<=belong[r]-1; i++)tag[i]+=val; //将整块需要加的值更新上去}int query(int l,int r,int val){ int ans=-1; for(int i=l; i<=min(belong[l]*m,r); i++)if(a[i]+tag[belong[l]]
::iterator it=S[i].lower_bound(val-tag[i]);//小于等于 if(it!=S[i].begin())ans=max(ans,*(--it)+tag[i]); } return ans;}int main(){ scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; //将每个值分到所给的块内 for(int i=1; i<=n; i++)scanf("%d",&a[i]),S[belong[i]].insert(a[i]); for(int i=1,op,l,r,val; i<=n; i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)change(l,r,val); else printf("%d\n",query(l,r,val)); } return 0;}

LOJ#6280. 数列分块入门 4

涉及区间求和,第一个代码xjb改改就好

#include
using namespace std;#define N 50005#define ll long longint m,belong[N];ll a[N],sum[N],tag[N];void change(int l,int r,int val){ for(int i=l;i<=min(r,belong[l]*m);i++) a[i]+=val,sum[belong[l]]+=val;//将a块内剩余部分暴力更新 if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1;i<=r;i++) a[i]+=val,sum[belong[r]]+=val;;//将b块内剩余部分暴力更新 } for(int i=belong[l]+1;i<=belong[r]-1;i++)tag[i]+=val;//将整块需要加的值更新上去}ll query(int l,int r)//和上面思路差不多{ ll ans=0; for(int i=l;i<=min(r,belong[l]*m);i++)ans+=a[i]+tag[belong[l]]; if(belong[l]!=belong[r]) for(int i=(belong[r]-1)*m+1;i<=r;i++)ans+=a[i]+tag[belong[r]]; for(int i=belong[l]+1;i<=belong[r]-1;i++) ans+=sum[i]+m*tag[i]; return ans;}int main(){ int n; scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;//将每个值分到所给的块内 for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[belong[i]]+=a[i]; for(int i=1,op,l,r,val;i<=n;i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)change(l,r,val); else printf("%d\n",query(l,r)%(val+1)); } return 0;}

LOJ#6281. 数列分块入门 5

区间开方,区间求和,怎么那么像呢,这个不是暴力线段树更新?

当然分块也能做啊,上面的题目也可以线段树刷的

#include
using namespace std;#define N 50005#define ll long longint m,belong[N];int a[N],sum[N],tag[N];void la(int x){ if(tag[x])return; tag[x]=1,sum[x]=0; for(int i=(x-1)*m+1; i<=x*m; i++) { a[i]=sqrt(a[i]),sum[x]+=a[i]; if(a[i]>1) tag[x]=0; }}void change(int l,int r){ for(int i=l; i<=min(r,belong[l]*m); i++) sum[belong[l]]-=a[i],a[i]=sqrt(a[i]),sum[belong[l]]+=a[i]; //将a块内剩余部分暴力更新 if(belong[l]!=belong[r]) { for(int i=(belong[r]-1)*m+1; i<=r; i++) sum[belong[r]]-=a[i],a[i]=sqrt(a[i]),sum[belong[r]]+=a[i]; //将b块内剩余部分暴力更新 } for(int i=belong[l]+1; i<=belong[r]-1; i++)la(i);//将整块需要加的值更新上去}int query(int l,int r)//和上面思路差不多{ int ans=0; for(int i=l; i<=min(r,belong[l]*m); i++)ans+=a[i]; if(belong[l]!=belong[r]) for(int i=(belong[r]-1)*m+1; i<=r; i++)ans+=a[i]; for(int i=belong[l]+1; i<=belong[r]-1; i++)ans+=sum[i]; return ans;}int main(){ int n; scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 for(int i=1; i<=n; i++) belong[i]=(i-1)/m+1; //将每个值分到所给的块内 for(int i=1; i<=n; i++) scanf("%d",&a[i]),sum[belong[i]]+=a[i]; for(int i=1,op,l,r,val; i<=n; i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)change(l,r); else printf("%d\n",query(l,r)); } return 0;}

LOJ#6282. 数列分块入门 6

给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。

vector的insert或者链表都可以吧,但是数据随机生成啊,就可以分块了

 

#include
using namespace std;#define N 200005#define ll long longint m,m1;int a[N],tag[N];vector
vec[N];pair
query(int x){ int now=1; while(x>vec[now].size())x-=vec[now].size(),now++; return make_pair(now,x-1);}void rebuild(){ int tot=0; for(int i=1; i<=m1; i++) { for(auto it:vec[i])tag[++tot]=it; vec[i].clear(); } int mt=sqrt(tot); for(int i=1; i<=tot; i++) { vec[(i-1)/mt+1].push_back(tag[i]); } m1=(tot-1)/mt+1;}void insert(int pos,int x){ pair
tmp=query(pos); vec[tmp.first].insert(vec[tmp.first].begin()+tmp.second,x); if(vec[tmp.first].size()>20*m)rebuild(); //重构}int main(){ int n; scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 for(int i=1; i<=n; i++) scanf("%d",&a[i]),vec[(i-1)/m+1].push_back(a[i]); m1=(n-1)/m+1; for(int i=1,op,l,r,val; i<=n; i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(!op)insert(l,r); else { pair
temp=query(r); printf("%d\n",vec[temp.first][temp.second]); } } return 0;}

LOJ#6283. 数列分块入门 7

有一个区间乘法,还有区间加法,当然这个也可以去暴力更新

更新的时候要注意下,就是我乘起来的更新了,你乘的时候加法的也需要啊

#include
using namespace std;#define MD 10007#define N 100005#define ll long longint m,n,belong[N];int a[N],atag[N],mtag[N];void reset(int x){ for(int i=(x-1)*m+1; i<=min(n,x*m); i++) a[i]=(a[i]*mtag[x]+atag[x])%MD; atag[x]=0,mtag[x]=1;}void add(int l,int r,int val){ reset(belong[l]); for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]+val)%MD; if(belong[l]!=belong[r]) { reset(belong[r]); for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]+val)%MD; } for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]+val)%MD;}void mul(int l,int r,int val){ reset(belong[l]); for(int i=l; i<=min(r,belong[l]*m); i++)a[i]=(a[i]*val)%MD; if(belong[l]!=belong[r]) { reset(belong[r]); for(int i=(belong[r]-1)*m+1; i<=r; i++)a[i]=(a[i]*val)%MD; } for(int i=belong[l]+1; i<=belong[r]-1; i++)atag[i]=(atag[i]*val)%MD,mtag[i]=(mtag[i]*val)%MD;}int main(){ scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1; for(int i=1; i<=belong[n]; i++)mtag[i]=1; for(int i=1,op,l,r,val; i<=n; i++) { scanf("%d%d%d%d",&op,&l,&r,&val); if(op==0)add(l,r,val); else if(op==1)mul(l,r,val); else printf("%d\n",(a[r]*mtag[belong[r]]+atag[belong[r]])%MD); } return 0;}

LOJ#6284. 数列分块入门 8

查询位于 [l,r]的数字有多少个是 c,再把位于 [l,r]的数字都改为 c

这个题目类似于第五个题目

每次都是修改区间,所以问题变得简单了起来

我每次修改一块的几个,那就是一块的复杂度,然后我只需要对这个块就行修改就好了

对于不完整块,只能把这个tag先更新一遍,再进行求解

tag==-1表示这个块的值不相同,如果改为c恰好是-1呢,所以这个值最好选一个没出现的值吧

所以我对分块有一点浅薄的认识,就是如果暴力来做复杂度很高,而且没有好的(或者说有你不会写)数据结构去维护,这个时候分块的话均摊的复杂度就不大,可以通过这种trick去搞这个题目

#include
using namespace std;#define MD 10007#define N 100005#define ll long longint m,n,belong[N];int a[N],tag[N];void reset(int x){ if(tag[x]==-1)return; for(int i=(x-1)*m+1; i<=min(n,x*m); i++)a[i]=tag[x]; tag[x]=-1;}int query(int l,int r,int val){ int ans=0; reset(belong[l]); for(int i=l; i<=min(r,belong[l]*m); i++) { if(a[i]!=val)a[i]=val; else ans++; } if(belong[l]!=belong[r]) { reset(belong[r]); for(int i=(belong[r]-1)*m+1; i<=r; i++) { if(a[i]!=val)a[i]=val; else ans++; } } for(int i=belong[l]+1; i<=belong[r]-1; i++) { if(tag[i]!=-1) { if(tag[i]!=val) tag[i]=val; else ans+=m; } else { for(int j=(i-1)*m+1; j<=i*m; j++) { if(a[j]!=val) a[j]=val; else ans++; } tag[i]=val; } } return ans;}int main(){ scanf("%d",&n); m=sqrt(n+0.5);//一块有几个 memset(tag,-1,sizeof tag); for(int i=1; i<=n; i++) scanf("%d",&a[i]),belong[i]=(i-1)/m+1; for(int i=1,l,r,val; i<=n; i++) { scanf("%d%d%d",&l,&r,&val); printf("%d\n",query(l,r,val)); } return 0;}

LOJ#6285. 数列分块入门 9

(「BZOJ2724」[Violet 6] 蒲公英)

给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。

最后一个难爆了,表示根本想不出来

应该是完整的所有块的众数,和不完整块中出现的数。

所以我们可以预处理f(i,j)表示第 i 块到第 j 块的众数(枚举 i 开个桶扫一遍)。

那么只要能快速得出一个数在某个区间内出现次数即可,每次只要比较至多2√n+1个元素的出现次数,这题就解决了。

由于没有修改,只要离散化以后,给每个数 x 开个vector,按顺序存下 x 出现的位置,每次询问 x 时把区间的左右端点放进对应 vector 二分一下即可。

根据均值不等式,可以算出分块大小大概是√(n/logn)

抄黄学长的博客,迷迷糊糊算是看懂了吧,感觉好难

#include
using namespace std;int n,m,tot;int v[50005],belong[50005];int f[505][505];map
M;int val[50005],cnt[50005];vector
vec[50005];void pre(int x){ memset(cnt,0,sizeof cnt); int mx=0,ans=0; for(int i=(x-1)*m+1,t; i<=n; i++) { cnt[v[i]]++,t=belong[i]; if(cnt[v[i]]>mx||(cnt[v[i]]==mx&&val[v[i]]
mx||(t==mx&&val[v[i]]
mx||(t==mx&&val[v[i]]

 

转载于:https://www.cnblogs.com/BobHuang/p/9632901.html

你可能感兴趣的文章
穆宁格:5.8 投资心态很重要,心态决定输赢
查看>>
每日一算 (001)
查看>>
python 闭包与装饰器
查看>>
引入 Tinker 之后如何在 Debug 模式下开启 Instant Run
查看>>
干货:小微个人如何接入免费短信验证码
查看>>
javaScript之全局函数
查看>>
阿里中后台UI解决方案 - Fusion
查看>>
HTTP常用状态码
查看>>
剑指offer 题目4
查看>>
重学前端(9)正则还真要多练
查看>>
MongoDB的复制源oplog
查看>>
五线谱入门(三)
查看>>
原创文章:使用Vuejs实现个人所得税功能兼容移动端
查看>>
HashiCorp:为任何应用程序提供安全和可运行的基础架构
查看>>
面试中经常被问到的 Redis 持久化与恢复
查看>>
好程序员大数据技术分享Zookeeper集群管理与选举
查看>>
Dell-Windows10下装Ubuntu 16.04 双系统,Ubuntu引导开启-经验贴,满干货!
查看>>
说说主流的推送服务
查看>>
加密狗只是开始,区块链+文娱才是大趋势
查看>>
一个vue-cli创建项目webpack相关都配置合简介
查看>>