BZOJ 2724蒲公英 (分块) 【内有块大小证明】
题面
分析
先分块,设块大小为x(之后我们会证明块大小取何值会更优)
步骤1
把所有的数离散化,然后对每个值开一个vector pos[i],pos[i]存储数i出现的位置
我们设查询的区间为[l,r],需要求数v出现的次数,然后在vector中二分查找出第一个>=l的数的位置p1,和第一个>r的数的位置p2,p2-p1即为数v出现的次数
例: 离散化后的数组a={1,3,3,2,3,1,3 },则pos[3]={2,3,5,7},因为第2,3,5个数为3 我们需要查找[2,6]中数3出现的次数,发现p1=2,p2=4,出现的次数即为2
步骤2
然后我们考虑询问,如果只记录每个块里的众数显然是不行的,因为我们需要把许多个块的结果合起来,而众数不满足区间可加性,无法在O(√n)的时间内完成结果合并
因此我们预处理出所有块端点组成的区间,mode[l][r],maxt[l][r],表示[第l个块的起点,第r个块的终点]这个区间里的众数和众数的出现次数
查询[l,r]时我们可以得到中间的一个由块端点组成的区间,可以直接通过刚刚的预处理得到众数和众数的出现次数
两边多余的部分直接用步骤1暴力即可
至于mode,maxt数组如何预处理,直接用两重for循环来实现
首先枚举块的起点i,然后j从i遍历到n,用一个临时数组cnt来记录每个数出现的次数,就可以求出区间[i,j]的众数,如果j正好是块端点,则记录答案
for(int i=1; i<=bl; i++) { int ans=INF; int tim=0; for(int j=lb(i); j<=n; j++) { cnt[a[j]]++; if(cnt[a[j]]>tim||(cnt[a[j]]==tim&&a[j]<ans)) { ans=a[j]; tim=cnt[a[j]]; } if(j%sz==0) { mode[i][j/sz]=ans; maxt[i][j/sz]=tim; }else if(j==n){ mode[i][bl]=ans; maxt[i][bl]=tim; } } for(int j=lb(i); j<=n; j++) cnt[a[j]]=0; }
时间复杂度分析
设块的大小为x,假设n为x的倍数
初始化部分:
从第一个块末尾需要遍历n-x次,从第二个块末尾需要遍历n-2x次,从第nx个块末尾需要遍历n−x·nx次
总的遍历次数为
其中12n可忽略,时间复杂度为O(n2x)
查询部分:
考虑查询的极端情况,查询[2,n-1]
则需要遍历左,右长度各为(x-1)的块(可近似看成x)
时间复杂度为O(xlogn)
m次询问O(mxlogn)
所以总时间复杂度为O(n2x+mxlogn)
根据均值不等式
当x=√n2mlogn时,总时间复杂度为2√n2mlogn=O(n√mlogn)
因此块大小为√n2mlogn时最优,由于n,m同级,可近似取√nlogn
代码
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<map> #include<cmath> #include<vector> #define maxn 100005 #define maxs 2005 #define INF 0x7fffffff using namespace std; inline void qread(int &x) { x=0; int sign=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') sign=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } x=x*sign; } inline void qread(long long &x) { x=0; long long sign=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') sign=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } x=x*sign; } inline void qprint(int x) { if(x<0) { putchar('-'); qprint(-x); } else if(x==0) { putchar('0'); return; } else { if(x/10>0) qprint(x/10); putchar('0'+x%10); } } int n,m,num,sz,bl; int id[maxn];//第i个位置属于的块编号 int a[maxn]; int b[maxn]; inline int lb(int id) {//求第id个块的左端点 return sz*(id-1)+1; } inline int rb(int id) {//求第id个块的右端点 return sz*id>n?n:sz*id; } vector<int>pos[maxn]; int get_count(int val,int l,int r) { return upper_bound(pos[val].begin(),pos[val].end(),r)-lower_bound(pos[val].begin(),pos[val].end(),l); } int cnt[maxn]; int mode[maxs][maxs]; int maxt[maxs][maxs]; void ini() { for(int i=1; i<=n; i++) { pos[a[i]].push_back(i); } // for(int i=1; i<=num; i++) { // sort(pos[b[i]].begin(),pos[b[i]].end()); // } for(int i=1; i<=bl; i++) { int ans=INF; int tim=0; for(int j=lb(i); j<=n; j++) { cnt[a[j]]++; if(cnt[a[j]]>tim||(cnt[a[j]]==tim&&a[j]<ans)) { ans=a[j]; tim=cnt[a[j]]; } if(j%sz==0) { mode[i][j/sz]=ans; maxt[i][j/sz]=tim; }else if(j==n){ mode[i][bl]=ans; maxt[i][bl]=tim; } } for(int j=lb(i); j<=n; j++) cnt[a[j]]=0; } } int query(int l,int r) { int ans=INF; int tim=0; if(id[l]+1<=id[r]-1) { ans=mode[id[l]+1][id[r]-1]; tim=maxt[id[l]+1][id[r]-1]; } for(int i=l; i<=min(r,rb(id[l])); i++) { int tmp=get_count(a[i],l,r); if(tmp>tim||(tmp==tim&&a[i]<ans)) { ans=a[i]; tim=tmp; } } if(id[l]!=id[r]) { for(int i=lb(id[r]); i<=r; i++) { int tmp=get_count(a[i],l,r); if(tmp>tim||(tmp==tim&&a[i]<ans)) { ans=a[i]; tim=tmp; } } } // return ans; return b[ans]; } int main() { int l,r; qread(n); qread(m); for(int i=1; i<=n; i++) { qread(a[i]); b[i]=a[i]; } sort(b+1,b+1+n); num=unique(b+1,b+1+n)-b-1; for(int i=1; i<=n; i++) { a[i]=lower_bound(b+1,b+1+num,a[i])-b; } sz=sqrt(n/(log(n)/log(2))); bl=1; for(int i=1; i<=n; i++) { id[i]=bl; if(i%sz==0) bl++; } int x=0; ini(); for(int i=1; i<=m; i++) { qread(l); qread(r); l=(l+x-1)%n+1; r=(r+x-1)%n+1; if(l>r) swap(l,r); x=query(l,r); qprint(x); putchar('\n'); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· 管理100个小程序-很难吗
· 在SqlSugar的开发框架中增加对低代码EAV模型(实体-属性-值)的WebAPI实现支持