传送门

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 ;
}

bzoj2724: [Violet 6]蒲公英(分块)的更多相关文章

  1. BZOJ2724 [Violet 6]蒲公英 分块

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ2724.html 题目传送门 - BZOJ2724 题意 求区间最小众数,强制在线. $n$ 个数,$m ...

  2. bzoj2724: [Violet 6]蒲公英 分块 区间众数 论algorithm与vector的正确打开方式

    这个,要处理各个数的话得先离散,我用的桶. 我们先把每个块里的和每个块区间的众数找出来,那么在查询的时候,可能成为[l,r]区间的众数的数只有中间区间的众数和两边的数. 证明:若不是这里的数连区间的众 ...

  3. [BZOJ2724][Violet 6]蒲公英

    [BZOJ2724][Violet 6]蒲公英 试题描述 输入 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 输出 输入示 ...

  4. 【BZOJ2724】[Violet 6]蒲公英 分块+二分

    [BZOJ2724][Violet 6]蒲公英 Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n ...

  5. BZOJ 2724: [Violet 6]蒲公英( 分块 )

    虽然AC了但是时间惨不忍睹...不科学....怎么会那么慢呢... 无修改的区间众数..分块, 预处理出Mode[i][j]表示第i块到第j块的众数, sum[i][j]表示前i块j出现次数(前缀和, ...

  6. 【分块】bzoj2724 [Violet 6]蒲公英

    分块,离散化,预处理出: ①前i块中x出现的次数(差分): ②第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现的次数,加上差分的结 ...

  7. 【bzoj2724】[Violet 6]蒲公英 分块+STL-vector

    题目描述 输入 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 输出 样例输入 6 3 1 2 3 2 1 2 1 5 3 ...

  8. bzoj2724: [Violet 6]蒲公英(离散化+分块)

    我好弱啊..这题调了2天QwQ 题目大意:给定一个长度为n(n<=40000)的序列,m(m<=50000)次询问l~r之间出现次数最多的数.(区间众数) 这题如果用主席树就可以不用处理一 ...

  9. BZOJ 2724: [Violet 6]蒲公英 [分块 区间众数]

    传送门 题面太美不忍不放 分块分块 这种题的一个特点是只有查询,通常需要预处理:加入修改的话需要暴力重构预处理 预处理$f[i][j]$为第i块到第j块的众数,显然$f[i][j]=max{f[i][ ...

随机推荐

  1. Oracle学习笔记_02_基本SQL

    1.select语句 (1)语法 SELECT *|{[DISTINCT] column|expression [alias],...} FROM table; (2)示例: 选择全部列 SELECT ...

  2. 使用SQL脚本创建数据库,操作主键、外键与各种约束(MS SQL Server)

    在实际开发中,可能很少人会手写sql脚本来操作数据库的种种.特别是微软的MS SQL Server数据库,它的SQL Server Management Studio对数据库的图形化操作极致简便,从而 ...

  3. C - Alyona and SpreadsheetDP

    题目链接 题意在一个矩阵中,询问l~r行是否有一列满足mp[i][j]>=mp[i-1][j](i属于l~r)即非递减序列,是输出Yes,否输出No 用vector<vector<i ...

  4. 绘图工具--turtle模块

    turtle模块主要使用两个类,一个是TurtleScreen类,表示画布(窗口),用来展示画的位置:一个是Turtle类,用来充当画笔,用来画. 两个类的方法也以同名的函数的形式存在,所以可以以面向 ...

  5. [冬令营模拟]GTSG2018

    上学期没有去 GTSG,于是今天老师让我们来做一下 GTSG2018 Day1 & Day3 Day1 在上午当成一场考试来搞了,Day3 由于锅太多而且 T3 玄学而被放到下午自学... 上 ...

  6. python之路-进程

    博客园 首页 新随笔 联系 管理 订阅 随笔- 31  文章- 72  评论- 115    python之路——进程   阅读目录 理论知识 操作系统背景知识 什么是进程 进程调度 进程的并发与并行 ...

  7. 移动端H5 button 默认事件

    button 在移动端下会有自带的默认事件,如果不处理的话,点击按钮的时候会有自动刷新页面的效果,原因就是button的默认事件没有阻止. 所以在点击事件里面要加上 e.preventDefault( ...

  8. virtual judge(专题一 简单搜索 A)

    问题描述: Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘, ...

  9. linux 时间处理 + 简单写log

    1s ==1000ms == 1,000,000us == 1,000,000,000 nanosecond uname -a Linux scott-Z170X 4.15.0-34-generic ...

  10. 加载某个页面(A)时实现自动跳转到某个页面(B)

    <head> <title></title> <script type="text/javascript"> function fu ...