传送门

md调了一个晚上最后发现竟然是空间开小了……明明算出来够的……

讲真其实我以前不太瞧得起分块,觉得这种基于暴力的数据结构一点美感都没有。然而今天做了这道分块的题才发现分块的暴力之美(如果我空间没有开小就更美了)

我们先将整个数组分块,设块的大小为$T$

我们先预处理出所有以块边界为端点的区间的答案,即$ans[L][R]$代表着第$L$块到第$R$块的序列所代表的答案。这个可以$O(n*n/T)$预处理

然后我们先将所有的数给离散化,然后对每一个值都开一个vector,记录这个值在数组中出现的每一个位置。比如数组的下标为1,3,5的位置都是3,那么3的vector记录的就是{1,3,5}

这个有什么用呢?我们设查询的区间为$[l,r]$,然后在这个vector里先二分查找第一个大于等于$l$的数的位置,再二分查找第一个大于$r$的数的位置,那么两个位置一减就是这个数在这个区间中的出现次数。比如查询区间$[2,4]$,我们先找到第一个大于等于2的数3,在vector中下标为2,再找第一个大于4的数为5,下标为3,那么3-2=1就是3这个数字在这个区间中的出现次数

那么,我们设$[L,R]$为查询区间之间的整块,因为我们第一步已经预处理出了所有块与块之间的答案,那么这一段之间的众数也就可以知道。那么,只有区间$[l,L-1]$和$[R+1,r]$之间的数字有可能更新答案。那么我们就去枚举这两个区间中的所有数字,然后用上面说的方法去查询它在整个查询区间内的出现次数,然后更新答案即可

复杂度为$O(n*n/T+n*T*logn)$,设块的大小为$n/sqrt{nlogn}$ ,那么时间复杂度就是$O(nsqrt{nlogn})$

其实还有一种更快的方法是先预处理出块与块之间的答案和各个数的出现次数,然后查询只要在散块里暴力增加并更新答案,之后暴力复原即可(然而我懒并不想打)

然后基本注意点都写在注解里了

 //minamoto
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(int x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=,M=;
int ans[M][M],a[N],b[N],cnt[N],rt[N],vis[N];
vector<int> pos[N];
int n,m,q,lastans=,s,l,r;
inline int query_cnt(int x){
//查询数的出现次数,注意l和r要开全局变量
return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l);
}
void init(){
//暴力枚举块与块之间的答案
for(int i=;i<=rt[n];++i){
memset(cnt,,sizeof(cnt));
int bg=s*(i-)+,res=a[bg];
for(int j=bg;j<=n;++j){
++cnt[a[j]];
if(cnt[a[j]]>cnt[res]||(cnt[a[j]]==cnt[res]&&a[j]<res)) res=a[j];
ans[i][rt[j]]=res;
}
}
}
int query(int l,int r){
//查询,小块暴力,大块直接找答案
if(rt[r]-rt[l]<=){
int id=,res=;
for(int i=l;i<=r;++i)
if(!vis[a[i]]){
int t=query_cnt(a[i]);
if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
vis[a[i]]=;
}
for(int i=l;i<=r;++i) vis[a[i]]=;
return b[id];
}
int L=rt[l]+,R=rt[r]-;
int LL=(L-)*s+,RR=R*s;
int id=ans[L][R],res=query_cnt(id);vis[id]=;
for(int i=l;i<LL;++i)
if(!vis[a[i]]){
int t=query_cnt(a[i]);
if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
vis[a[i]]=;
}
for(int i=RR+;i<=r;++i)
if(!vis[a[i]]){
int t=query_cnt(a[i]);
if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
vis[a[i]]=;
}
for(int i=l;i<LL;++i) vis[a[i]]=;
for(int i=RR+;i<=r;++i) vis[a[i]]=;
vis[ans[L][R]]=;
return b[id];
}
int main(){
n=read(),q=read(),s=sqrt(n/(double)(log2(n))+);
//我怕s会变成0所以sqrt里加了个1(可能并不需要)
for(int i=;i<=n;++i) a[i]=b[i]=read(),rt[i]=(i-)/s+;//分块
sort(b+,b++n),m=unique(b+,b++n)-b-;
for(int i=;i<=n;++i) a[i]=lower_bound(b+,b++m,a[i])-b,pos[a[i]].push_back(i);
//以上是离散
init();
while(q--){
l=read(),r=read();
l=(l+lastans-)%n+,r=(r+lastans-)%n+;
if(l>r) swap(l,r);
print(lastans=query(l,r));
}
Ot();
return ;
}

最新文章

  1. vfp 智能感知拓展应用
  2. yii验证码不使用model在控制器中进行验证
  3. disable_irq()与disable_irq_nosync()区别
  4. I.MX6 show battery states in commandLine
  5. 【 D3.js 高级系列 — 7.0 】 标注地点
  6. JS中的== 、===的用法和区别。
  7. FZU-Problem 2294 Uint47 calculator
  8. idea不显示gradle的视图解决办法
  9. 利用foo函数的Bof漏洞攻击:构造攻击字符串
  10. python 中range函数的用法
  11. windows server 2008 R2 部署NFS,实现多台服务器间、客户端间的共享目录。
  12. 8 -- 深入使用Spring -- 3...1 Resource实现类ServletContextResource
  13. elasticsearch插件的开发--计算特征向量的相似度
  14. 在CentOS7上部署PostgreSQL11数据库系统
  15. Dropout caffe源码
  16. Android中XML解析-PULL解析
  17. 教你如何修改FireFox打开新标签页(NewTab Page)的行列数
  18. 仿微信小红圈消息提示App消息红圆点提示
  19. 替换Android系统镜像system.img的方法
  20. Python自定义大小截屏

热门文章

  1. name lookup of &#39;res&#39; changed for new ISO &#39;res&#39; scoping
  2. numpy.ndarray类型的数组元素输出时,保留小数点后4位
  3. opensource mcu
  4. luoguP1941福赖皮波德
  5. A唐纳德先生和假骰子(华师网络赛)
  6. windows下,CSV文件Excel打开乱码
  7. JVM体系结构之六:堆Heap之1
  8. HttpApplication 对象的创建过程及HttpModule过滤器的内部实现过程
  9. 将List中部分字段转换为DataTable中
  10. %.*s, printf