数列分块入门(最后一个难啊)
感觉这个不算科技吧,就是现在把操作降在块内了,算是暴力的优化
https://loj.ac/problem/6277
LOJ6277. 数列分块入门 1
对于这些数你有两种操作,0是区间+,1是区间查询
#include<bits/stdc++.h> using namespace std; #define N 50005 int 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<bits/stdc++.h> using namespace std; #define N 50005 int n,m,belong[N]; int a[N],tag[N]; vector<int>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]]<val)ans++; if(belong[l]!=belong[r]) for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans++; for(int i=belong[l]+1; i<=belong[r]-1; i++) ans+=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin(); 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*val)); } return 0; }
LOJ6279. 数列分块入门 3
询问区间内小于某个值 x的前驱(比其小的最大元素)
这个上面改改就行了
#include<bits/stdc++.h> using namespace std; #define N 100005 int n,m,belong[N]; int a[N],tag[N]; vector<int>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]]<val)ans=max(ans,a[i]+tag[belong[l]]); if(belong[l]!=belong[r]) for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]); for(int i=belong[l]+1,pos; i<=belong[r]-1; i++) { pos=lower_bound(vec[i].begin(),vec[i].end(),val-tag[i])-vec[i].begin(); if(pos>=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<stdio.h> #include<bits/stdc++.h> using namespace std; #define N 100005 int n,m,belong[N]; int a[N],tag[N]; set<int>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]]<val)ans=max(ans,a[i]+tag[belong[l]]); if(belong[l]!=belong[r]) for(int i=(belong[r]-1)*m+1; i<=r; i++)if(a[i]+tag[belong[r]]<val)ans=max(ans,a[i]+tag[belong[r]]); for(int i=belong[l]+1; i<=belong[r]-1; i++) { set<int>::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<bits/stdc++.h> using namespace std; #define N 50005 #define ll long long int 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<bits/stdc++.h> using namespace std; #define N 50005 #define ll long long int 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<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long int m,m1; int a[N],tag[N]; vector<int>vec[N]; pair<int,int>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<int,int>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<int,int>temp=query(r); printf("%d\n",vec[temp.first][temp.second]); } } return 0; }
LOJ#6283. 数列分块入门 7
有一个区间乘法,还有区间加法,当然这个也可以去暴力更新
更新的时候要注意下,就是我乘起来的更新了,你乘的时候加法的也需要啊
#include<bits/stdc++.h> using namespace std; #define MD 10007 #define N 100005 #define ll long long int 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<bits/stdc++.h> using namespace std; #define MD 10007 #define N 100005 #define ll long long int 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<bits/stdc++.h> using namespace std; int n,m,tot; int v[50005],belong[50005]; int f[505][505]; map<int,int>M; int val[50005],cnt[50005]; vector<int>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]]<val[ans]))ans=v[i],mx=cnt[v[i]]; f[x][t]=ans; } } int query(int l,int r,int x) { int t=upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l); return t; } int query(int a,int b) { int ans,mx; ans=f[belong[a]+1][belong[b]-1]; mx=query(a,b,ans); for(int i=a,t; i<=min(belong[a]*m,b); i++) { t=query(a,b,v[i]); if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t; } if(belong[a]!=belong[b]) for(int i=(belong[b]-1)*m+1,t; i<=b; i++) { t=query(a,b,v[i]); if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t; } return ans; } int main() { scanf("%d",&n); m=200; //直接给定块大小,这个比较优秀,这个要根据均值不等式求得,在分块中,不同的块大小设置很影响程序的快慢 for(int i=1; i<=n; i++) { scanf("%d",&v[i]); if(!M[v[i]])M[v[i]]=++tot,val[tot]=v[i]; v[i]=M[v[i]],vec[v[i]].push_back(i); } for(int i=1; i<=n; i++)belong[i]=(i-1)/m+1; for(int i=1; i<=belong[n]; i++)pre(i); for(int i=1,l,r; i<=n; i++) { scanf("%d%d",&l,&r); printf("%d\n",val[query(l,r)]); } return 0; }
本文来自博客园,作者:暴力都不会的蒟蒻,转载请注明原文链接:https://www.cnblogs.com/BobHuang/p/9632901.html